When passing data to a Blade component, what is the difference between type="error", :message="$message" and ::class, and how does @props decide what is a prop?
answer
- plain attribute: always a string
- colon prefix: a PHP expression
- double colon: leave it for JavaScript
- :$roomId short syntax
- @props names leave the bag
basics
~20 sA plain attribute passes a string, a colon attribute passes a PHP expression's value, and a double colon emits a single-colon attribute for JavaScript. Names in @props or the constructor become variables; the rest stay in $attributes.
solid answer
~40 s`<x-alert type="error">` passes the string `"error"`. `<x-alert :message="$message" :dismissible="true">` evaluates PHP, so `$dismissible` is the boolean `true` - whereas `dismissible="false"` would be the truthy string `"false"`. `:$roomId` is short for `:room-id="$roomId"`. `::class="{ open: isOpen }"` tells Blade the value is not PHP and renders `:class="..."` for Alpine. On an anonymous component, `@props(['type' => 'info', 'message'])` pulls those keys out of the attributes into variables - using the array value as a default when the caller omits it - and leaves the rest in `$attributes`; on a class component the constructor parameters play that role. Props are raw values, so echo them with `{{ }}`.
code
html · 15 lines{{-- resources/views/components/confirm-modal.blade.php --}}
@props(['title', 'bookingId', 'refundable' => false])
<div x-data="{ open: false }" {{ $attributes->merge(['class' => 'modal']) }}>
<h2>{{ $title }}</h2>
<p>Booking #{{ $bookingId }}</p>
@if ($refundable)
<p>The guest will be refunded.</p>
@endif
{{ $slot }}
</div>
{{-- usage --}}
<x-confirm-modal title="Cancel booking" :booking-id="$booking->id"
:refundable="$booking->isRefundable()" ::class="{ 'is-open': open }" class="z-50" />go deeper
Recall that plain attributes pass strings and colon attributes pass PHP values, with kebab-case names on the tag.
Explain how @props turns named attributes into variables with defaults and rebuilds $attributes from the rest, and what :: and :$var do.
Catch string-boolean bugs, missing defaults and class-as-prop mistakes in review, and echo props with {{ }} since they are raw values.
Define prop conventions for a shared component set - typed constructor props or documented @props defaults - so misuse fails loudly.
## Three ways to write an attribute on a component tag A Blade component tag looks like HTML, but Blade reads its attributes as **data**. The prefix decides how: | Syntax | What the component receives | |---|---| | `type="error"` | the string `error` | | `:message="$notice->body"` | the value of the PHP expression | | `:dismissible="true"` | the boolean `true` | | `dismissible="false"` | the string `false`, which is truthy | | `:$roomId` | same as `:room-id="$roomId"` | | `::class="{ open: isOpen }"` | rendered as `:class="{ open: isOpen }"`, not evaluated | Three rules follow: 1. **Plain attributes are strings.** Use them for fixed, author-written values. A number or boolean needs the colon. 2. **Colon attributes are PHP.** The quoted text is an expression evaluated in the parent view, so `:max-guests="$room->capacity"` passes an integer. 3. **Double colon escapes the colon.** Alpine.js and similar libraries use `:attr` for their own bindings. `::` tells Blade to output a literal single-colon attribute instead of evaluating PHP. Attribute names are kebab-case on the tag and camelCase in PHP: `max-guests` reaches the constructor parameter `$maxGuests`, and an anonymous component declaring `'maxGuests'` in `@props`. ## How @props splits props from attributes An anonymous component has no constructor, so it declares its props at the top of the template: ```html @props(['type' => 'info', 'message', 'dismissible' => false]) ``` At render time the compiled `@props` code: - walks the incoming attributes; any key named in the list becomes a local variable (`$type`, `$message`, `$dismissible`); - builds a new `$attributes` bag from the keys that were **not** named; - gives each string-keyed entry its default when the caller did not pass it. An entry without a default (`'message'`) is still a prop - it leaves the bag - but if the caller omits it, echoing `$message` fails as an undefined variable. Give optional props a default, often `null`. On a **class-based** component, the constructor parameters are the props. Blade resolves parameter names from the class and removes them from the bag automatically. ## Escaping: props versus the bag Props arrive as **raw PHP values**. The component must print them with `{{ $message }}`, never `{!! $message !!}` unless it is HTML the component trusts. Values bound with `:` that stay in the **attribute bag** are escaped when the tag compiles, which is why `{{ $attributes }}` is safe to print. ## A hotel-admin modal ```html <x-confirm-modal title="Cancel booking" :booking-id="$booking->id" :refundable="$booking->isRefundable()" ::class="{ 'is-open': open }" class="z-50" /> ``` With `@props(['title', 'bookingId', 'refundable' => false])`, the modal gets a string title, an integer id and a real boolean; `class` and the Alpine binding stay in the bag and land on the root element. ## Checking inputs on a class component A class component gets PHP's own help: typed constructor parameters (`public int $bookingId`, `public bool $refundable = false`) document the interface, defaults live in one place, and a missing required parameter fails when the component is resolved rather than as an undefined variable deep in the template. That is one practical reason to promote a heavily used component from anonymous to class-based. ## Mistakes interviewers look for - Passing `:open="false"`-style booleans without the colon, then wondering why `@if ($open)` is always true. - Writing `:title="Cancel booking"`, which Blade evaluates as PHP and fails to compile. - Using camelCase attributes (`bookingId="..."`) and getting a missing prop. - Forgetting a default for an optional prop and hitting an undefined variable. - Declaring `class` as a prop, which pulls it out of the bag so it is no longer merged.
- Why does <x-toggle enabled="false"> leave @if ($enabled) true?Without a colon the attribute is the string `"false"`, and a non-empty string other than `"0"` is truthy in PHP. Write `:enabled="false"` so Blade evaluates the expression and passes the boolean `false`.
- What does @props(['message']) do when the caller omits message?`message` is still treated as a prop, so it is kept out of `$attributes`, but no default is assigned. Echoing `$message` then fails as an undefined variable. Give optional props a default, such as `@props(['message' => null])`, and branch on it.
saying these in an interview costs you the question
- enabled="false" passes the boolean false to a component
- A colon attribute's value is treated as a literal string
- ::class is evaluated as PHP like :class
- Props listed in @props also stay in $attributes
- Constructor parameters are matched by camelCase attribute names