skip to content

In nested Blade components such as <x-modal size="lg"> containing <x-modal.footer>, how does @aware let the child read the parent's props, and why can it miss the parent's defaults?

level: seniorimportance: nice to knowfreq 22%

answer

  1. child reads parent component data
  2. looks up the component data stack
  3. only attributes the caller passed
  4. parent @props defaults are invisible
  5. give @aware its own default

basics

~20 s

@aware(['size' => 'md']) lets a child read size from the data of its enclosing components. For an anonymous parent that data is only what the caller wrote on the tag, not @props defaults, so the child needs a matching default.

solid answer

~40 s

A child component cannot normally see its parent's props: `<x-modal size="lg">` passes `size` to the modal, not to `<x-modal.footer>` inside it. Declaring `@aware(['size' => 'md'])` in the child compiles to a lookup through the view factory's component data stack: it checks the child's own data, then each enclosing component's, and falls back to the default. The catch is what that stack holds for an anonymous parent - the attributes the caller **explicitly passed** to its tag. If the modal's `@props(['size' => 'lg'])` default applies because the caller wrote no `size`, `@aware` never sees `lg` and the footer uses its own `md`. Keep the parent and child defaults identical, or pass the value explicitly.

code

html · 12 lines
html
{{-- components/modal/modal.blade.php --}}
@props(['size' => 'lg'])
<div {{ $attributes->class(['modal', 'modal-'.$size]) }}>{{ $slot }}</div>

{{-- components/modal/footer.blade.php --}}
@aware(['size' => 'lg'])  {{-- mirror the parent's default --}}
<footer @class(['modal-footer', 'gap-6' => $size === 'lg'])>{{ $slot }}</footer>

{{-- usage --}}
<x-modal>
    <x-modal.footer>...</x-modal.footer>   {{-- no size passed: footer uses its own default --}}
</x-modal>

go deeper

for a junior

Recall that child components do not see parent props unless the child declares them with @aware.

for a middle

Explain that @aware walks the component data stack from the nearest parent and falls back to its own default.

for a senior

Diagnose the mismatched-default bug where the parent's @props default never reaches the child, and centralise or mirror defaults.

for a principal

Limit @aware to shared presentation settings in compound components and keep business data explicit at call sites.

## The problem @aware solves Compound components - a modal with a header, body and footer; a menu with items - often share a setting such as size or colour. The caller sets it once on the outer tag: ```html <x-modal size="lg"> <x-modal.body>...</x-modal.body> <x-modal.footer>...</x-modal.footer> </x-modal> ``` Blade components are **isolated**: `<x-modal.footer>` receives only its own attributes and slots, so it knows nothing about `size`. Passing `size` again to every child is repetitive and error-prone. ## What @aware does In the child template: ```html @aware(['size' => 'md']) <footer {{ $attributes->class(['modal-footer', 'gap-4' => $size === 'lg']) }}> {{ $slot }} </footer> ``` `@aware` compiles to a loop that, for each listed key, calls the view factory's `getConsumableComponentData()`: 1. If the child's own data has the key, use it. 2. Otherwise walk the stack of components currently being rendered, from the nearest parent outwards, and return the first match. 3. If none has it, return the given default (`'md'`). The result is assigned to a local variable (`$size`). Unlike `@props`, `@aware` does not remove anything from the child's attribute bag. ## Why parent defaults are invisible The component data stack holds the data each component was started with. For an **anonymous** parent that is the attributes the caller wrote on its tag. A default declared inside the parent with `@props(['size' => 'lg'])` is applied later, when the parent's own template runs, as a local variable in that template; it is never written back to the stack. (A **class-based** parent is different: its data is its public properties, so a constructor default such as `public string $size = 'lg'` is visible to `@aware`.) So: | Caller writes | Modal's `$size` | Footer's `@aware` `$size` | |---|---|---| | `<x-modal size="lg">` | `lg` | `lg` | | `<x-modal size="sm">` | `sm` | `sm` | | `<x-modal>` (modal default `lg`) | `lg` | **`md`** - its own default | The last row is the classic bug: the modal renders large while its footer renders medium spacing. The Laravel docs call this out explicitly: `@aware` cannot access parent data that was not explicitly passed to the parent via HTML attributes. ## Fixes - **Mirror the default.** Use the same default in the parent's `@props` and every child's `@aware`. Simple, but two places to keep in sync. - **Centralise the default.** Read it from one PHP constant or config value in both places. - **Pass it explicitly** where it matters, accepting a little repetition. - **Make the parent a class component.** Its public properties, constructor defaults included, are part of the data `@aware` reads. ## When @aware is the right tool - Shared visual settings in compound components: size, tone, density, colour. - Values the caller normally sets on the outer tag. It is a poor fit for business data (the booking being cancelled) - pass that explicitly, so the child's inputs stay visible at the call site - and for deep, far-apart nesting where a reader cannot tell which ancestor supplies the value. ## @aware compared with @props | | `@props` | `@aware` | |---|---|---| | Reads from | the component's own tag | enclosing components' data | | Removes the key from `$attributes` | yes | no | | Default applies when | the caller omits the attribute | no ancestor passed it | | Typical use | the component's interface | settings shared down a compound component | A child can list the same name in both, so it accepts `size` directly but falls back to its parent's value. ## Testing it Render the parent with and without the attribute and assert on the child's output, for example with `Blade::render('<x-modal><x-modal.footer>ok</x-modal.footer></x-modal>')`. The no-attribute case is the one that exposes mismatched defaults.

  • Does @aware remove the key from the child's attribute bag like @props does?
    No. `@aware` only assigns a local variable from the component data stack or its default. If the child should also treat that name as a prop so it does not leak into `$attributes`, declare it in `@props` as well.
  • If both the modal and an intermediate panel component received size, which does the footer see?
    The nearest one. The lookup checks the child's own data first, then walks enclosing components from the closest parent outwards and returns the first that has the key. An intermediate component's explicitly passed value shadows the outer modal's.

saying these in an interview costs you the question

  • @aware reads the parent's @props default values
  • Child components automatically see all of their parent's props
  • @aware removes the key from the child's attribute bag
  • @aware looks at the outermost component first
  • Business data should be shared through @aware to avoid repetition