Custom Formatters

Shorten or clean the title and description before the snippet draws them, with a closure in your config.

The section clamps a long title to one line and a long description to two, the way Google does. A formatter changes the text itself instead – truncate it at the length a search result actually shows, strip the HTML a textarea leaves behind, or vary it per page.

Defining a Formatter

Formatters live under johannschopplich.serp-preview.formatters, one for title and one for description. Each is a closure that receives the resolved value and the page, and returns what the snippet should draw:

site/config/config.php
use Kirby\Toolkit\Str;

return [
    'johannschopplich.serp-preview' => [
        'formatters' => [
            'title' => fn (string $value, \Kirby\Cms\Page $page) => Str::short($value, 60),
            'description' => fn (string $value, \Kirby\Cms\Page $page) => Str::short(strip_tags($value), 160)
        ]
    ]
];

Define only the one you need – a missing formatter leaves that line untouched.

What a Formatter Sees

The value arrives already resolved, in the order Title and Description Resolution lists, so a formatter never sees a {{ ... }} placeholder.

The page is the one being edited, which is what makes a formatter page-specific. On the site view it is the home page; on a page's file view the formatter does not run and the snippet draws the value as it is:

site/config/config.php
'title' => function (string $value, \Kirby\Cms\Page $page) {
    // Articles carry the section in front of the title, other pages do not.
    if ($page->intendedTemplate()->name() === 'article') {
        return $page->parent()->title() . ': ' . $value;
    }

    return $value;
}
A formatter runs on the server, so it can reach anything Kirby can – other pages, files, the site.

Where It Stops

A formatter shapes what the snippet shows and nothing else. It does not touch the content, the saved page, or the markup your frontend renders – if you want the same truncation in your <title> tag, call the same helper there.

A formatter runs again as the editor types, at most once per 250 ms. One that throws shows no error in the Panel: the snippet keeps the text it last drew, and the failed request is only in the browser's network tab. A formatter that fetches a remote URL delays the snippet, not the typing.