---
title: "Meta Tags"
description: "Render description, Open Graph, Twitter Card, JSON-LD, robots, and canonical tags for a page from its fields, its page model, or global defaults."
canonical_url: "https://kirby.tools/docs/helpers/meta-tags"
---

# Meta Tags

> Render description, Open Graph, Twitter Card, JSON-LD, robots, and canonical tags for a page from its fields, its page model, or global defaults.

The `meta()` page method renders the tags a page needs in its `<head>`. It renders no `<title>` – that stays in your template:

```php [site/snippets/head.php]
<?php $meta = $page->meta() ?>
<title><?= $page->title()->escape() ?></title>
<?= $meta->robots() ?>
<?= $meta->social() ?>
<?= $meta->jsonld() ?>
```

`social()` reads a `description` field and a `thumbnail` files field:

```yaml [site/blueprints/pages/default.yml]
fields:
  description:
    type: textarea
  thumbnail:
    type: files
    multiple: false
```

With both filled in, `social()` renders:

<code-collapse>

```html
<meta
  content="An exhibition of street photography from the 1970s."
  name="description"
/>
<meta content="Kirby Gallery" property="og:site_name" />
<meta content="https://example.com/exhibitions/street" property="og:url" />
<meta content="website" property="og:type" />
<meta content="Street" property="og:title" />
<meta
  content="An exhibition of street photography from the 1970s."
  property="og:description"
/>
<meta
  content="https://example.com/media/pages/exhibitions/street/2f6c1e7a90-1791524069/poster-1200x.jpg"
  property="og:image"
/>
<meta content="1200" property="og:image:width" />
<meta content="800" property="og:image:height" />
<meta content="Two people crossing a street" property="og:image:alt" />
<meta content="summary_large_image" name="twitter:card" />
<meta content="Street" name="twitter:title" />
<meta
  content="An exhibition of street photography from the 1970s."
  name="twitter:description"
/>
<meta
  content="https://example.com/media/pages/exhibitions/street/2f6c1e7a90-1791524069/poster-1200x.jpg"
  name="twitter:image"
/>
<meta content="Two people crossing a street" name="twitter:image:alt" />
```

</code-collapse>

## Where Values Come From

A value such as `description`, `thumbnail`, `robots`, or `canonical` is looked up in this order, and the first one set wins:

<steps level="3">

### The Page Model's `metadata()`

An array returned by a `metadata()` method on the [page model](https://getkirby.com/docs/guide/templates/page-models).

### The `meta.defaults` Option

See [Global Defaults](#global-defaults).

### The Page's Field

A field of the same name, if it is not empty.

### The Site's Field

A field of the same name in the site content, if it is not empty.

</steps>

## Page Model

```php [site/models/article.php]
<?php

use Kirby\Cms\Page;

class ArticlePage extends Page
{
    public function metadata(): array
    {
        return [
            'description' => $this->text()->excerpt(160)->value(),
            'thumbnail' => fn (Page $page) => $page->cover(),
            'opengraph' => [
                'type' => 'article'
            ],
            'jsonld' => [
                'BlogPosting' => [
                    'headline' => $this->title()->value(),
                    'datePublished' => $this->date()->toDate('c')
                ]
            ]
        ];
    }
}
```

A value can be a closure that receives the page. `thumbnail` takes a file ID, a UUID, or a files field returned from a closure, as in the example above – a files field set directly, or a `File` object, throws once `social()` runs.

## Global Defaults

`meta.defaults` takes an array, or a closure that receives `$kirby`, `$site`, and `$page`:

```php [site/config/config.php]
return [
    'johannschopplich.helpers' => [
        'meta' => [
            'defaults' => fn ($kirby, $site, $page) => [
                'jsonld' => [
                    'WebSite' => [
                        'url' => $site->url(),
                        'name' => $site->title()->value()
                    ]
                ]
            ],
            'twitter' => [
                'site' => '@kirbytools',
                'creator' => '@johannschopplich'
            ]
        ]
    ]
];
```

`meta.twitter.site` and `meta.twitter.creator` render as `twitter:site` and `twitter:creator`.

A key in `meta.defaults` beats the page field of that name on every page – only `metadata()` sits above it. For a site-wide fallback, use a field of that name in the site content instead. Write keys in lowercase; a camelCase key is never found.

The `opengraph`, `twitter`, `meta`, and `jsonld` arrays come only from `metadata()` and `meta.defaults`, merged key by key: a page model adds entries to the defaults and replaces only the keys it sets.

## Generated Tags

### `social()`

Every tag below can be set in the `meta`, `opengraph`, or `twitter` array instead, and a value you set wins:

- **description**: the `description` value, for the `description` meta tag and both networks.
- **og:site_name**, **og:url**, **og:type**: the site title, the page URL, and `website`.
- **og:title**, **twitter:title**: the page's `customTitle` field, else its title. This reads the page field directly, so set `opengraph.title` and `twitter.title` to change it from `metadata()`.
- **og:image**: the first file of the `thumbnail` value, resized to at most 1200 pixels wide, with its width, height, and `alt` field. An `opengraph.image` of your own replaces the thumbnail together with its width, height, and alt text – set those yourself.
- **twitter:image**, **twitter:image:alt**: the Open Graph image and its alt text.
- **twitter:card**: `summary_large_image`, or `summary` when there is no image – also when you set `summary_large_image` yourself.

Further tags go into the `meta` array, one `name` per key:

```php [site/config/config.php]
return [
    'johannschopplich.helpers' => [
        'meta' => [
            'defaults' => [
                'meta' => [
                    'author' => 'Kirby Gallery'
                ]
            ]
        ]
    ]
];
```

### `robots()`

A `robots` meta tag when the `robots` value is set, and a canonical link to the `canonical` value or the page URL.

### `jsonld()`

One `<script type="application/ld+json">` per entry of the `jsonld` array. The key becomes `@type`, and `@context` is `https://schema.org`; both can be overridden in the entry.

### `opensearch()`

A `<link rel="search">` to `/open-search.xml`, titled with the site title. The plugin does not serve that file – add it, or a route for it, yourself.

## Nested and Namespaced Properties

An array value in `opengraph` or `twitter` expands into one tag per key: `image` with a `width` key becomes `og:image:width`, or `twitter:image:width`. `'image' => ['width' => 1200]` and `'image:width' => 1200` address the same tag; where both are set, the flat key wins.

A `namespace:` prefix replaces the `og:` part, which is how `article:*` and `product:*` tags are written. It applies to `opengraph` only. In a page model's `metadata()`:

```php
'opengraph' => [
    'type' => 'article',
    'namespace:article' => [
        'published_time' => $this->date()->toDate('c'),
        'author' => 'Johann Schopplich'
    ]
]
```

```html
<meta content="article" property="og:type" />
<meta content="2026-10-09T00:00:00+00:00" property="article:published_time" />
<meta content="Johann Schopplich" property="article:author" />
```

## Read Values

`$page->meta()->get('description')` returns any value as a field, looked up in the order above; `false` as the second argument skips the site field. Any other method name reads the value of that name, so `$page->meta()->description()` is the same call. `priority()` returns the [sitemap](/docs/helpers/sitemap#what-it-lists) priority as a float.

---

Every page of this site as Markdown: <https://kirby.tools/sitemap.md>
