Skip to content

[6.x] Adds Blade Component Support to Antlers#12424

Merged
jasonvarga merged 3 commits intostatamic:masterfrom
JohnathonKoster:antlers-blade-components
Sep 23, 2025
Merged

[6.x] Adds Blade Component Support to Antlers#12424
jasonvarga merged 3 commits intostatamic:masterfrom
JohnathonKoster:antlers-blade-components

Conversation

@JohnathonKoster
Copy link
Contributor

This PR adds Blade component support to Antlers. With the changes in this PR,
template authors can now write code like the following, directly in their Antlers templates:

{{ collection:pages }}
    <x-card :$title />
{{ /collection:pages }}

Authoring Blade Components

In addition to using Blade components within your Antlers templates, you may now author Blade components using Antlers. To help with this, Antlers now supports the @props and @aware directives.

When authoring Blade components using Antlers, the @props and @aware directives accept Antlers code, not PHP.

As an example, the following applies an Antlers modifier to the default title value:

@props([
  'title' => 'the default title' | upper
])

The Title: {{ title }}

You may also use attributes, just like in Blade:

<div {{ attributes.merge([
    'class' => 'extra classes to be merged'
]) }}>
    {{ slot }}
</div>

Additional Modifiers

This PR also includes two new modifiers, to help improve the experience when authoring components.

The first is the is_string modifier, which checks if the provided value is a string:

{{ if slot | is_string }}

{{ /if }}

The second is the has_actual_content modifier, which checks if the provided value contains any actual content. This logic is the same as Blade's {{ $slot->hasActualContent() }} method:

{{ if slot | has_actual_content }}

{{ /if }}

The modifier is an alternative to calling the method, which also works:

{{ if slot->hasActualContent() }}

{{ /if }}

Component Scope

Unlike when using partials, components do not inherit the current scope. Custom variables, and variables created within the components are not shared with the inner or outer scope (except for slots).

{{#
    _my_var will not be avaiable _inside_ the component
#}}
{{ _my_var = 'the value'; }}

<x-alert :$title>
    {{# But will work inside the slot. #}}
    {{ _my_var /}}
</x-alert>

You may also access the current component instance inside the slot content, just like in Blade:

<x-alert>
    {{ component.componentName }}
</x-alert>

@jasonvarga
Copy link
Member

This is awesome.

One thing I noticed though is that there is a little bit of scope inheritance.

Take this example, where I'm on the home page and don't provide the title as a prop.

{{ title }}

{{ collection:articles }}
  <x-card />
{{ /collection:articles }}
{{# card.antlers.html #}}
<div>{{ title }}</div>

You get this:

Home

<div>Home</div>
<div>Home</div>
<div>Home</div>

It happens regardless of whether I define @props in the component.

@JohnathonKoster
Copy link
Contributor Author

JohnathonKoster commented Sep 23, 2025

The extra {{ title }} was coming from the Cascade itself. I've adjusted this PR to shut down the Cascade entirely when inside these components, and have added support for the @cascade directive, as well, to opt-in to data.

@jasonvarga jasonvarga merged commit b33c560 into statamic:master Sep 23, 2025
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants