In a Blade modal component, how do the default $slot and named slots such as <x-slot:footer> work, including optional slots and slot attributes?
answer
- unnamed content fills $slot
- x-slot:name or x-slot name=
- a slot is a ComponentSlot object
- isEmpty() and hasActualContent()
- $footer->attributes->class()
basics
~20 sContent between a component's tags that is not inside an x-slot becomes $slot; each x-slot:name becomes a $name variable. Slots are ComponentSlot objects with isEmpty(), hasActualContent() and their own attributes bag, and an optional slot needs a default.
solid answer
~40 sIn `<x-modal>`, markup not wrapped in a slot tag fills `$slot`; `<x-slot:title>` (or `<x-slot name="title">`) fills `$title`, and so on. Each slot is an `Illuminate\View\ComponentSlot`, which is `Htmlable`, so `{{ $title }}` prints its already-compiled markup without re-escaping. `$slot->isEmpty()` tests for no content and `hasActualContent()` ignores HTML comments. A slot tag can carry attributes - `<x-slot:footer class="justify-end">` - read through `$footer->attributes`, a normal attribute bag supporting `class()` and `merge()`. A named slot the caller leaves out is simply undefined, so declare it with a default, e.g. `@props(['footer' => null])`, and test it before rendering the footer. Inside a slot, `$component` reaches the class component's public methods.
code
html · 20 lines{{-- resources/views/components/modal.blade.php --}}
@props(['footer' => null])
<div role="dialog" {{ $attributes->merge(['class' => 'modal']) }}>
<header class="modal-title">{{ $title }}</header>
<div class="modal-body">
@if ($slot->hasActualContent())
{{ $slot }}
@else
<p>No details provided.</p>
@endif
</div>
@if ($footer)
<footer {{ $footer->attributes->class(['modal-footer', 'flex']) }}>
{{ $footer }}
</footer>
@endif
</div>go deeper
Recall that loose content fills $slot and x-slot:name fills a matching variable, echoed with {{ }}.
Explain ComponentSlot, isEmpty() versus hasActualContent(), slot attributes via ->attributes, and how optional slots need defaults.
Design modal and card APIs that choose props versus slots deliberately, handle missing slots cleanly and use $component for scoped data.
Keep a component library's slot contracts documented and consistent so product teams compose screens without reading each template.
## What slots are A **slot** is a block of markup the caller writes between a component's opening and closing tags, which the component places somewhere in its own template. Props carry values; slots carry **content**. A hotel-admin modal needs three content areas - a title, a body and a row of footer buttons - and slots let each caller fill them differently. ## The default slot and named slots ```html <x-modal> <x-slot:title>Cancel booking #4821</x-slot> <p>The guest will be notified by email.</p> <x-slot:footer class="justify-end"> <x-button type="button">Keep</x-button> <x-button type="submit" class="btn-danger">Cancel booking</x-button> </x-slot> </x-modal> ``` Inside the component: - `$slot` holds everything not inside an `x-slot` tag - here the paragraph. - `$title` and `$footer` hold the named slots. `<x-slot name="title">` is the older, equivalent spelling of `<x-slot:title>`. Slot content is compiled in the **caller's** view, so `{{ }}` echoes inside it are escaped there, and the caller's variables are available inside the slot. ## Slots are objects Each slot is an `Illuminate\View\ComponentSlot`: | Member | Use | |---|---| | `toHtml()` / echo | the compiled markup; `Htmlable`, so `{{ $title }}` does not escape it again | | `isEmpty()` | true when the content is an empty string | | `isNotEmpty()` | the inverse | | `hasActualContent()` | true when something other than whitespace and HTML comments is present | | `attributes` | an attribute bag built from the slot tag's attributes | `hasActualContent()` matters when a caller leaves a comment placeholder in a slot: `isEmpty()` says false, but there is nothing to show. ## Optional named slots If a caller omits `<x-slot:footer>`, the component has **no `$footer` variable at all**. Echoing it throws an undefined-variable error. Two safe patterns: 1. Declare it with a default: `@props(['footer' => null])`, then `@if ($footer) ... @endif`. 2. Guard with `@isset($footer)`. For the default slot, `$slot` always exists, so `$slot->isEmpty()` is enough. ## Slot attributes Attributes on a slot tag let the caller tweak the element the component wraps the slot in: ```html <footer {{ $footer->attributes->class(['modal-footer', 'flex']) }}> {{ $footer }} </footer> ``` The caller's `class="justify-end"` is joined with the component's classes, exactly like the component-level `$attributes` bag. ## Scoped access with $component For a **class-based** component, markup inside a slot can call the component's public members through `$component`: ```html <x-availability-modal check-in="2026-10-01" check-out="2026-10-03"> <x-slot:title>{{ $component->nights() }} nights</x-slot> </x-availability-modal> ``` This is Blade's equivalent of scoped slots: the caller writes the markup, the component provides the data. ## Slots inside a class component's render() A class component's `render()` may return a closure instead of a view; the closure can receive a `$data` array with `componentName`, `attributes` and `slot`, and returns a Blade string. The Laravel docs warn never to embed those `$data` elements directly into the returned Blade string: the string is compiled as a template, so attribute or slot content spliced into it could be executed as Blade and PHP. Reference `{{ $attributes }}` and `{{ $slot }}` inside the template text instead, and let Blade render them as data. ## Choosing slots or props - Short, plain text such as a title string: a prop is simpler and can be typed. - Rich markup, other components, or content that varies by caller: a slot. - A modal commonly takes the title as a slot when some screens need an icon or a badge in it. ## Mistakes to watch for - Echoing an optional named slot without a default. - Printing `{!! $slot !!}`; unnecessary, because the slot is already `Htmlable`. - Using `isEmpty()` where comment placeholders are possible; use `hasActualContent()`. - Expecting the component's variables inside the slot; slot content sees the **caller's** scope, and only `$component` bridges to the class.
- Why does {{ $title }} not escape the markup inside the title slot?A slot is an `Illuminate\View\ComponentSlot`, which implements `Htmlable`, and Blade's `e()` returns an `Htmlable`'s `toHtml()` unescaped. The slot's content was already compiled in the caller's view, where its own `{{ }}` echoes were escaped, so printing it raw here is correct.
- Can markup inside a slot use the caller's variables?Yes. Slot content is compiled and rendered in the caller's view, so it sees the caller's variables, such as `$booking`. It does not see the component's internal variables; for a class-based component, `$component` exposes the component's public properties and methods to the slot.
saying these in an interview costs you the question
- An omitted named slot is an empty $footer variable
- Slots must be printed with {!! !!} to render their HTML
- isEmpty() ignores HTML comments in a slot
- Slot content can read the component's local variables
- Only the default slot can receive attributes