skip to content

In a Blade view, what do @class, @style, @checked and @selected compile to, and what hand-written template code do they replace?

level: middleimportance: should knowfreq 42%

answer

  1. conditional attributes without inline ifs
  2. @class writes the whole class attribute
  3. array key is class, value is condition
  4. @checked echoes checked when true
  5. values are not HTML-escaped

basics

~20 s

@class and @style take an array of always-on entries plus entry => condition pairs and write a complete class or style attribute; @checked, @selected, @disabled, @readonly and @required print the bare attribute when their condition is true.

solid answer

~30 s

`@class(['card', 'card--current' => $plan->is($current), 'opacity-50' => ! $plan->available])` compiles to `class="<?php echo Arr::toCssClasses(...) ?>"`: numeric keys always apply, string keys apply when their value is truthy, joined with spaces. It writes the whole attribute, so you never write `class="@class(...)"`. `@style` does the same through `Arr::toCssStyles`, adding a trailing `;` to each entry. `@checked($cond)` compiles to `if ($cond) echo 'checked'`, and `@selected`, `@disabled`, `@readonly` and `@required` follow the same pattern. They replace inline `@if`/ternaries inside tags. One caution: the class and style strings are not HTML-escaped, so keep keys literal rather than built from user input.

code

html · 20 lines
html
<div @class([
    'plan-card',
    'plan-card--current' => $plan->is($currentPlan),
    'opacity-50' => ! $plan->available,
])>
    <h3>{{ $plan->name }}</h3>
</div>

<label>
    <input type="checkbox" name="annual" value="1" @checked(old('annual', $isAnnual))>
    Bill annually
</label>

<select name="plan">
    @foreach ($plans as $plan)
        <option value="{{ $plan->slug }}" @selected(old('plan', $currentPlan->slug) === $plan->slug)>
            {{ $plan->name }}
        </option>
    @endforeach
</select>

go deeper

for a junior

Recall that @class takes always-on and conditional entries and writes the whole attribute, and @checked or @selected print the attribute when true.

for a middle

Explain the compiled output through Arr::toCssClasses and Arr::toCssStyles, and how old() with @checked repopulates forms after validation.

for a senior

Spot the unescaped output when class or style values come from user data, and move variant logic into components or PHP.

for a principal

Agree on where presentation logic lives: literal utility classes in directives, variants in components, rules in PHP, so templates stay readable and safe.

## The problem they solve Conditional attributes written by hand clutter a tag and invite spacing and quoting bugs: ```html <div class="card {{ $plan->is($current) ? 'card--current' : '' }} {{ $plan->available ? '' : 'opacity-50' }}"> <option value="pro" {{ $selected === 'pro' ? 'selected' : '' }}>Pro</option> ``` Blade provides directives that turn these into data. ## @class and @style `@class` takes an array. Entries with a **numeric key** are always included; entries with a **string key** are included when their value is truthy: ```html <div @class([ 'card', 'card--current' => $plan->is($currentPlan), 'opacity-50' => ! $plan->available, ])> ``` Compiled, it becomes `class="<?php echo \Illuminate\Support\Arr::toCssClasses([...]); ?>"`. Points worth knowing: - The directive emits the **entire `class="..."` attribute**. Writing `class="@class(...)"` produces a nested, broken attribute. - An array entry may hold several classes in one key (`'border ring-2' => $featured`). - With no argument, it produces an empty `class=""`. `@style` works the same way through `Arr::toCssStyles`, which appends `;` to each entry that lacks one: `@style(['color: gray', 'font-weight: bold' => $featured])` yields `style="color: gray; font-weight: bold;"`. ## @checked, @selected and friends These print a **boolean attribute** - an HTML attribute whose presence alone means true: | Directive | Compiled PHP | Typical use | |---|---|---| | `@checked($c)` | `if ($c): echo 'checked'; endif;` | checkboxes, radios | | `@selected($c)` | `... echo 'selected' ...` | `<option>` | | `@disabled($c)` | `... echo 'disabled' ...` | buttons, inputs | | `@readonly($c)` | `... echo 'readonly' ...` | inputs | | `@required($c)` | `... echo 'required' ...` | inputs | Combined with the `old()` helper, they re-populate a form after a failed submit: `@checked(old('annual', $subscription->annual))` ticks the box from the last submission, falling back to the stored value. ## A pricing table example A plan picker page shows cards and a billing form: 1. Each card uses `@class` to highlight the visitor's current plan and dim unavailable plans. 2. The "annual billing" checkbox uses `@checked(old('annual', $isAnnual))`. 3. The plan `<select>` marks the current plan with `@selected(old('plan', $current->slug) === $plan->slug)`. 4. The submit button uses `@disabled(! $user?->canUpgrade())` for visitors who cannot change plans. ## The escaping caution `Arr::toCssClasses` and `Arr::toCssStyles` **join strings; they do not HTML-escape them**, and the compiled directive echoes the result without `e()`. With literal keys that is harmless. If a class name or style value is built from user data - a "theme" field, a colour from a profile - a double quote in that data can close the attribute and inject new ones. Keep keys literal, map user choices to an allow-list (`'theme-'.$allowedTheme`), or cast numbers before interpolating. `@checked` and its siblings print fixed words, so their risk is only in the condition, not the output. ## Reading the compiled output Looking at the compiled view in `storage/framework/views` makes the behaviour obvious. `@class([...])` becomes a literal `class="` followed by a PHP echo of `Arr::toCssClasses(...)` and a closing quote; `@checked($x)` becomes `<?php if($x): echo 'checked'; endif; ?>`. There is no escaping step and no attribute merging: the directives are thin, predictable shortcuts, which is why they are easy to review and why their inputs must be literal or allow-listed. ## When not to use them - A single static class needs no directive. - Complex, reusable variant logic (size, tone, state) belongs in a component, where the attribute bag can merge classes passed by the caller; that is component material. - Business rules - who may upgrade, which plan is available - belong in PHP; the directive should receive a boolean, not compute one from several models.

  • What goes wrong with class="base @class(['active' => $on])"?
    `@class` emits a complete `class="..."` attribute, so the output is a class attribute containing another class attribute, which browsers misparse. Put every class in the array instead: `@class(['base', 'active' => $on])`.
  • Why is @class(['theme-'.$user->theme => true]) risky?
    The directive joins the keys with spaces and echoes them without HTML escaping. A theme value containing a double quote can close the class attribute and add attributes such as an event handler. Map the stored value to an allow-list of known themes before it reaches the view.

saying these in an interview costs you the question

  • @class goes inside an existing class="" attribute
  • @class escapes class names like {{ }} does
  • @checked prints checked="false" when the condition is false
  • @style requires each entry to end with a semicolon
  • These directives are only usable inside components