Adding lazy loading attribute to KirbyText (image: …)

I realise there are multiple ways to skin a cat, but I’d like to add lazy loading on my site in the simplest way possible. All modern browsers support loading=“lazy” so I was wondering if there’s a way to add custom attributes to the KirbyText (image:…) feature?

I was thinking I could have something like (image: mypic.png loading: lazy alt: My picture) which would then render in html as <img src=“mypic.png” alt=“My picture” loading=“lazy” />

Is this possible? I know I can implement lazy loading with JavaScript, but it seems like overkill to me, and I’d really prefer to have no JS on my site.



This page has the following example:

<?php if ($image = $page->image()): ?>
        loading="lazy" alt="<?= $image->alt() ?>"
        src="<?= $image->url() ?>"
        height="<?= $image->height() ?>"
        width="<?= $image->width() ?>"
<?php endif ?>

Which I think would work well for my use case as lazy loading would be automatically added, but I’m not clear on where this code should be added? Somewhere in the template I assume, but it’s not clear to me where this would live.

Yes, that’s possible but not out of the box. You would have to overwrite the image Kirbytag and add the attribute.

In particular the part: KirbyTags | Kirby CMS

See also example of extended image tag in the repo:

Thanks for the quick reply. In hindsight, I’d prefer to go with automatically adding loading="lazy" whenever an image is rendered within an article. Like I said above this code seems appropriate, but I’m not sure where to add it. I tried adding it to my template but that just ended up rendering the image twice - once where I put the php at the tope of the post and again within the post.

Well, if you use the image KirbyTag, then adding this code to your template doesn’t make sense, you need it inside what is rendered by the tag. So even if you don’t want the extra attribute for the image Kirbytag, then you have to change what this tag renders nonetheless.

However, adding lazy to all images might not be a good idea, because images above the fold should not be lazy loaded.

Ah yeah, of course. In that case, would it be possible to add a checkbox to my image blueprint for “lazy loading” that, when true, injects loading="lazy" into the image attribute?

Not sure if I understand your question. Yes, you can add such a checkbox, and yes, you can then query this attribute in the html part of your image tag.

But again, in most cases you wouldn’t add the attribute based on the image, but on the position of the image in the page, no?

What could be an alternative to adding the attribute every time, would be to default to lazy loading and then set to false for those cases where the image is used above the fold.

Yeah, that’s probably not a good solution, actually.

So I’ve been playing around with adding a plugin to extend the image attribute, but I’m in over my head and clearing doing something wrong (again).

I’ve create /site/plugins/lazy-loading/index.php with the following content:


Kirby::plugin('lazy/loading', [
    'tags' => [
        'image' => [
            'attr' => [
            'html' => function($tag) {
                return 'loading="' . $tag->loading . '"';

Then where I’ve got an image on the page I’ve added (image: some-image.webp loading: lazy) but it’s not working. Instead of an image with the lazy loading attribute, I just see a string of text saying loading="lazy"

This has to return the complete html that you need to render the image, please see the example in the repo. Or use the original image tag as reference and work from there.

Thanks for being so patient with me @texnixe. I work in InfoSec, I’m not a developer (does it show?!)

Anyway, I managed to get it working, but I have a feeling I’ve probably got more noise than is needed to actually make this thing work. I’ll play around to see if there’s anything I can cut out in the future. But for now, it’s working.

Here’s the entire index.php from the plugin:


Kirby::plugin('lazy/loading', [
    'tags' => [
        'image' => [
            'attr' => [
            'html' => function ($tag) {
                if ($tag->file = $tag->file($tag->value)) {
                    $tag->src     = $tag->file->url();
                    $tag->alt     = $tag->alt     ?? $tag->file->alt()->or(' ')->value();
                    $tag->title   = $tag->title   ?? $tag->file->title()->value();
                    $tag->caption = $tag->caption ?? $tag->file->caption()->value();
                    $tag->loading = $tag->loading ?? $tag->file->loading()->value();
                } else {
                    $tag->src = Url::to($tag->value);
                $link = function ($img) use ($tag) {
                    if (empty($tag->link) === true) {
                        return $img;
                    $link   = $tag->file($tag->link)?->url();
                    $link ??= $tag->link === 'self' ? $tag->src : $tag->link;
                    return Html::a($link, [$img], [
                        'rel'    => $tag->rel,
                        'class'  => $tag->linkclass,
                        'target' => $tag->target,
                        'loading' => $tag->loading
                $image = Html::img($tag->src, [
                    'width'  => $tag->width,
                    'height' => $tag->height,
                    'class'  => $tag->imgclass,
                    'title'  => $tag->title,
                    'alt'    => $tag->alt ?? ' ',
                    'loading' => $tag->loading
                if ($tag->kirby()->option('kirbytext.image.figure', true) === false) {
                    return $link($image);
                // render KirbyText in caption
                if ($tag->caption) {
                    $options = ['markdown' => ['inline' => true]];
                    $caption = $tag->kirby()->kirbytext($tag->caption, $options);
                    $tag->caption = [$caption];
                return Html::figure([ $link($image) ], $tag->caption, [
                    'class' => $tag->class