Skip to content

Michael LaRoy - Home

Creating Custom Tags in Statamic


As I’ve been exploring Statamic and its capabilities, I have come to really appreciate the Antlers templating system. Using its tags to render data in the template is about as easy as it gets. My curiosity in understanding how to generate them for use in my templates has led me to try and recreate a tag for outputting a collection.

Let’s explore together how and why we might use our own tags. First, let’s start with the obvious question… what is a “tag”?

What is a Tag?

play tag

Essentially, Tags in Statamic are “Antlers expressions giving you the ability to fetch, filter, and display content”

Behind the scenes, we have a mechanism in the form of some PHP code to fetch and return content. This PHP class extends the Tag class from Statamic’s internals, and allows us to decide what content to fetch and return, and in the middle somewhere we can manipulate that content any way we might like.

To illustrate, here’s a sample of the “collection” tag, which uses blog as a method to narrow down what we might want from the collection. In this case, the blog method will fetch an array of blog posts, and these tags are available in our Antlers templates:

<!-- the collection tag -->

<ul>
    {{ collection:blog }}
        <li>
            <a href="{{ url }}">{{ title }}</a>
        </li>
    {{ /collection:blog }}
</ul>

Making our own

You might ask why we would want to make our own, when we already have such flexible tags available to us? Well, there could be any number of reasons, including wanting to fetch content from outside of our Statamic project that we want available as data provided to Antlers. For a more simple example, perhaps I don’t want all the logic for my template to be in the template itself, but abstracted away in the PHP code instead, or to extend what parameters or methods are available by default.

Either way, let’s explore how to get started. In the project root, run this in the terminal. Here, “Blog” is the name of the Tag we want to create:

php please make:tag Blog

This command will create a new file in App/Tags/Blog.php, with at least something like the following:

namespace App\Tags;

use Statamic\Tags\Tags;

class Blog extends Tags
{
    public function index()
    {
        // logic to get content goes here

        // return something;
    }
}

When this new tag in our Antlers template, the index method is what will be called:

<!-- the new blog tag in an Antlers file -->

<ul>
    {{ blog }}
        <!-- Output stuff from the blog tag here -->
    {{ /blog }}
</ul>

Let’s start building this out simply, by just fetching some entries from our “blog” collection. We’ll need to use the Entry facade to retrieve them:

namespace App\Tags;

use Statamic\Tags\Tags;
use Statamic\Facades\Entry; // <-- we'll need this now

class Blog extends Tags
{
    public function index()
    {
        // create a query to get entries from
        // our blog collection
         $entries = Entry::query()
            ->where('collection', 'blog')
            ->orderBy('date', 'desc')
            ->get();

        return $entries;
    }
}

If you have any experience with Laravel (which Statamic sits on top of), then you might recognize this approach to getting data in a controller, and returning it to the View. Even their documentation says you’ll feel right at home if you think of these as Laravel Eloquent Models.

Extending Tags with a method

Perhaps the purpose for this tag is to fetch my blog posts by a particular taxonomy, without having to make my Antler’s too complicated. In this case, I would add a new method called “taxonomy” to my new Blog tag:


public function taxonomy()
{
    $category = $this->params->get('category');

    $entries = Entry::query()
        ->where('collection', 'blog')
        ->whereTaxonomy('categories::' . $category)  // <-- this is the new part
        ->orderBy('date', 'desc')
        ->get();

    return $entries;
}

In my template, we would use the new tag like so:

<!-- the new blog tag in an Antlers file -->

<ul>
    {{ blog:taxonomy category="popular"}}
        <!-- Output stuff from the blog tag here -->
    {{ /blog:taxonomy }}
</ul>

Using blog:taxonomy in the Antlers template ensures we use the “taxonomy” method from our Blog tag, instead of the “index” method. Here, we have also introduced referencing params, which is extra data passed into the method via the template. In the above example, the $category variable will be the string popular.

Combining this with the whereTaxonomy clause, we can use the the term’s slug to build our query. This would equate to:

->whereTaxonomy('categories::popular')

This could be extended with different params for different taxonomies, and your logic should probably look for them when deciding how to create this part of the query. Here, we are assuming “category” when we hardcode that into the whereTaxonomy clause. Read more about getting data with taxonomy terms in the docs

Putting it all together

Now we have a new (basic) working Blog tag (code below), with its own “taxonomy” method, which can be used in place of the Collection tag, once we flesh it out properly with pagination, and all the rest.

If we wish, we can create new methods or leverage different params to achieve the outcome we desire, so long as we have a good reason for making our own.

Come back next time where we will explore the “context” within tags, so we can understand what variables are available from our tags in the Antlers template.

namespace App\Tags;

use Statamic\Tags\Tags;
use Statamic\Facades\Entry;

class Blog extends Tags
{
    /**
     * The {{ blog }} tag.
     */
    public function index()
    {
        $entries = Entry::query()
            ->where('collection', 'blog')
            ->orderBy('date', 'desc')
            ->get();

        return $entries;
    }

    /**
     * The {{ blog:taxonomy }} tag.
     */
    public function taxonomy()
    {
        $category = $this->params->get('category');

        $entries = Entry::query()
            ->where('collection', 'blog')
            ->whereTaxonomy('categories::' . $category)
            ->orderBy('date', 'desc')
            ->get();

        return $entries;
    }
}

5 Accessibility Fixes You Can Make Today

Learn about the most common reasons that websites fail accessibility standards, and what you can do about it.