Blade Templates

Antlers is not always the best template engine for the job. If you're using Statamic as a headless CMS or want to share views with a Laravel application already using Blade or another engine, you can do that.

Overview

While Statamic's Antlers template language is powerful, tightly integrated, and simple to learn, it's not the only way to build your frontend views.

Antlers combines the responsibilities of Blade Templates and Controllers all at once. If you choose to not use Antlers, you may need to create controllers and routes to fetch content and map them to templates depending on what you're doing. Want to write Antlers in your Blade templates? That's also possible by using the @antlers Blade directive.

How to Render a Template with Blade

Instead of naming your views myview.antlers.html use myview.blade.php extension.

View Data

You will have access to the same data as you would in Antlers views.

Current Page

The current page's data will be available in a $page variable, and you can access its values using a syntax similar to Eloquent models.

---
title: My First Breakdance Competition
moves:
- Toprock
- 6-step
- Windmill
- L-kick
- Headspin
---
I did not win but I did have a good time.
<h1>{{ $page->title }}</h1>
 
<p>First I did
@foreach ($page->moves as $move)
{{ $move }}, then I did
@endforeach
and it was sick.</p>
 
{{ $page->content }}
Hot Tip!

Antlers outputs unescaped values by default, while {{ $content }} in Blade will be escaped. If you need to output unescaped HTML, use {!! $content !!}

Hot Tip!

When on a custom route, the $page variable won't be available in your view.

Globals

There is a variable for each global set, and its fields can be accessed using the same Eloquent style syntax.

# content/globals/settings.yaml
data:
site_name: Rad City
{{ $settings->site_name }}

System Variables

Top level system variables like, environment, logged_in, etc will be available as dedicated variables.

{{ $environment }}
@if ($logged_in) ... @endif

Relationships / Queries

Some fieldtypes (e.g. entries) will supply their data as query builders. These will work similar to Eloquent models, too.

If you use property access, it will resolve the query builder and get the items.

@foreach ($page->related_posts as $post)
{{ $post->title }}
@endforeach

If you use a method, it will give you a query builder and allow you to chain clauses on it.

@foreach ($page->related_posts()->where('title', 'like', '%awesome%')->get() as $post)
{{ $post->title }}
@endforeach

Cascade Directive

When using blade components or rendering views loaded by non-Statamic routes/controllers the cascade data will be not avaliable by default. In these situations you can use the @cascade directive to populate the current scope with cascade data.

It works in exactly the same way as the @props directive used in blade components, with the ability to require certain values and provide default fallback values:

@cascade([
'site', // Will throw an exception if missing from the cascade
'my_global',
'page' => null, // Will use fallback if missing from the cascade
'live_preview' => false,
])
 
{{ $site->locale }}
{{ $page?->title }}

It is also possible to populate the current scope with all cascade data if needed:

@cascade

Writing Pure Antlers in Blade 🆕

By using the @antlers and @endantlers Blade directive pair you can write pure Antlers in your Blade templates.

@antlers
{{ collection:articles }}
{{ title }}
{{ /collection:articles }}
@endantlers

Under the hood, this is syntactic sugar for creating an Antlers partial and does an on-the-fly @include('antlers_file_name_here') for you. This means that variables created inside the Antlers will not be available outside of the @antlers directive.

Using Antlers Blade Components

Despite the name, Antlers Blade Components are a Blade-only feature that allows you to use existing tags inside your Blade templates using a custom tag syntax. For example, you can gather all entries from a "pages" collection using the collection tag like so:

<s:collection:pages>
{{ $title }}
</s:collection:pages>

In the previous example, you may have noticed the s: prefix. To use Antlers Blade Components we must prefix the tag name with either s: or statamic: (s- and statamic- also work).

We can pass multiple parameters to the tag like so:

<s:collection
from="pages"
limit="2"
sort="title:desc"
>
{{ $title }}
</s:collection>

We can also pass dynamic values to parameters:

@php
$collection = 'pages';
@endphp
 
<s:collection
:from="$collection"
limit="2"
sort="title:desc"
>
{{ $title }}
</s:collection>

Antlers Blade Components and Partials

Partials also work with Antlers Blade Components, and are intended to be used when you'd like to include an Antlers partial inside your Blade template:

<s:partial/the-partial-name>
<s:slot:header>The header content.</s:slot:header>
 
Default slot content.
</s:partial/the-partial-name>
Hot Tip!

If you are going all-in on Blade for a new project, you should consider sticking to Blade features such as @include or Blade components instead of reaching for the partial tag.

Using Fluent Tags with Blade

You can use Tags in Blade templates with a Laravel-style fluent syntax. Instantiate your tag with the Statamic::tag() method and chain parameters as needed.

@foreach(Statamic::tag('collection:pages')->limit(3) as $page)
<li>{{ $page->title }}</li>
@endforeach
• Home
• Gallery
• Contact
Hot Tip!

When using multi-word parameters, like query_scope, you must use the camelCased version (queryScope).

Using Explicit Parameter Setters

If you need to set a parameter containing a colon (ie. a filter param), you can use the dedicated param() setter method:

Statamic::tag('collection:pages')->param('title:contains', 'pizza')

Or even set multiple parameters at once using the plural params() method:

Statamic::tag('collection:pages')->params([
'title:contains' => 'pizza',
'description:contains' => 'lasagna',
])

Passing Contextual Data

You can pass in contextual data to the tag using the context($data) method:

Statamic::tag('collection:pages')->context($context)

Fetching the output

When you loop over a tag or cast it to a string, it will automatically fetch the result for you. In some cases, you may want to explicitly fetch the output. You can do that with the fetch method.

@php($output = Statamic::tag('collection:pages')->fetch())

Pagination

For tags that provide pagination, you can fetch the tag's output in a variable, then output the results and links separately:

@php($tag = Statamic::tag('collection:pages')->paginate(2)->as('pages')->fetch())
 
@foreach($tag['pages'] as $page)
<li>{{ $page->title }}</li>
@endforeach
 
{{ $tag['paginate']['auto_links'] }}

Directive

You may prefer to use an alternate syntax, available via a @tags Blade directive.

Passing a string will give you the camelCased version of the tag as a variable:

@tags('collection:blog')
 
@foreach($collectionBlog as $post) ... @endforeach

Passing an array of tags can provide multiple variables:

@tags(['collection:blog', 'collection:items'])
 
@foreach($collectionBlog as $post) ... @endforeach
 
@foreach($collectionItems as $item) ... @endforeach

You may also pass an array of tags, and parameters, with variable names as the keys:

@tags([
'posts' => ['collection:blog' => ['limit' => 5]],
'items' => ['collection:items' => ['limit' => 5]],
])
 
@foreach($posts as $post) ... @endforeach
 
@foreach($items as $item) ... @endforeach

Using Modifiers with Blade

You can also use Modifiers in Blade templates with a Laravel-style fluent syntax. Wrap your value with the Statamic::modify() method and chain modifiers as needed. The value will get passed along in sequence like it does in Antlers. Any parameters should be specified like regular PHP parameters. If you use a modifier that can take more than one parameter, pass those in as an array.

{{ Statamic::modify($content)->striptags()->backspace(1)->ensureRight('!!!') }}
{{ Statamic::modify($content)->stripTags()->safeTruncate([42, '...']) }}
THIS IS THE FIRST POST, HOW EXCITING!!!
I wanted to say more but got cut off...
Hot Tip!

When using multi-word modifiers, like ensure_right, you must use the camelCased version (ensureRight).

If you're using a lot of modifiers in your Blade template, you can also include the modify helper function in your template:

@php
use function Statamic\View\Blade\{modify};
@endphp
 
{{ modify('test')->stripTags()->backspace(1)->ensureRight('!!!') }}
{{ modify('test')->stripTags()->safeTruncate([42, '...']) }}

Conditional Logic and Values

Depending on where you got a value from, it may be wrapped in a class like Value. These can lead to unexpected results in conditional logic if they are not handled correctly (i.e., calling ->value() in the condition).

If you'd prefer to not think about that, you can include the value helper function into your template, which will take care of this for you:

@php
use function Statamic\View\Blade\{value};
@endphp
 
@if (value($theVariableName))
...
@endif

The value helper function will handle the following scenarios for you:

  • Statamic\Fields\Value objects (calls ->value())
  • Statamic\Fields\Values objects (calls ->all())
  • Statamic\Tags\FluentTag objects (calls ->fetch())
  • Statamic\Modifiers\Modify objects (calls ->fetch())

Layouts

When Statamic attempts to render a URL (eg. an entry), two views are combined. A template gets injected into a layout's template_content variable.

When the template is not an Antlers view, this rule doesn't apply. The layout is ignored, allowing you to use @extends the way you would expect.

{{-- mytemplate.blade.php --}}
 
@extends('layout')
 
@section('body')
The body content
@endsection
{{-- mylayout.blade.php --}}
 
<html>
<body>
@yield('body')
</body>
</html>
<html>
<body>
The body content
</body>
</html>

This rule only applies to the template. You're free to use a .antlers.html template and a .blade.php layout. If you want to do this, the contents of the template will be available as in the template_content variable instead of yield.

{{# mytemplate.antlers.html #}}
 
The template content
{{-- mylayout.blade.php --}}
 
{!! $template_content !!}
<html>
<body>
The template contents
</body>
</html>

Passing Context into Components

If you are using Blade components for your layout rather than Blade directives, you might want to pass the view context into your layout for access by child components. You can do so with the special $__data variable in the layout root, and the @aware directive in the child. Here's how:

First, add a context prop to your layout component.

{{-- resources/views/components/layout.blade.php --}}
@props(['context'])
<html>
{{-- whatever you want to put in here... --}}
</html>

Then, merge the context prop with the parent data in your Layout component's data method.

<?php
 
namespace App\View\Components;
 
use Illuminate\View\Component;
 
class Layout extends Component
{
/**
* Create a new component instance.
*
* @return void
*/
public function __construct(public $context)
{
$this->context = $context;
}
 
public function data()
{
return array_merge(parent::data(), $this->context);
}
 
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
return view('components.layout');
}
}

Next, pass in the magic $__data variable from your template to your layout.

{{-- resources/views/default.blade.php --}}
<x-layout :context="$__data">
<x-hero />
</x-layout>

Last, use the @aware directive in any child component of your layout to access the variables from the cascade within your component.

{{-- resources/views/components/blade.php --}}
@aware(['page'])
<div>
{{ $page->hero_headline }}
</div>

Routes and Controllers

If you choose to take a more "traditional" Laravel application approach to building your Statamic site, you can use routes and controllers much the same way you might with Eloquent models instead of Statamic's native collection routing and data cascade. Here's an example:

The Routes

use App\Http\Controllers\BlogController;
 
Route::get('/blog', [BlogController::class, 'index']);
Route::get('/blog/{slug}', [BlogController::class, 'show']);

The Controller

<?php
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Statamic\Facades\Entry;
 
class BlogController extends Controller
{
public function index()
{
$entries = Entry::query()
->where('collection', 'blog')
->take(10)
->get();
 
return view('blog.index', ['entries' => $entries]);
}
 
public function show($slug)
{
$entry = Entry::query()
->where('collection', 'blog')
->where('slug', $slug)
->first();
 
return view('blog.show', ['entry' => $entry]);
}
}

The Index View

<h1>The Blog</h1>
 
<div class="grid md:grid-cols-3 gap-3">
@foreach($entries as $entry)
<a href="{!! $entry->url !!}" class="p-2 rounded shadow-sm">
<img src="{!! $entry->featured_image !!}" class="w-full">
<h2>{!! $entry->title !!}</h2>
<div>{!! $entry->date->format('Y-m-d') !!}</div>
</a>
@endforeach
</div>

The Show View

<header>
<h1>{!! $title !!}</h1>
<div>{!! $entry->date->format('Y-m-d') !!}</div>
</header>
<div class="mt-8 prose">
<img src="{!! $entry->featured_image !!}" class="w-full">
{!! $entry->content !!}
</div>
Docs feedback

Submit improvements, related content, or suggestions through Github.

Betterify this page →