Destiner's Notes

Astro: Extracting Common Meta Tags Into a Separate File

4 February 2022

At the start of creating a content site, you usually have one or two layouts, but after some time, you’ll likely add more. All of these layouts share common metadata, usually located in layout’s <head> tag.

It’s possible to manage all of that metadata inside each page separately, but that might quickly turn into a mess. Imagine you want to update analytics, or add a new metatag for SEO. Updating each and every layout will require more time and might lead to errors.

In line with the DRY principle, let’s put that metadata inside a separate file and use it across all our pages and layouts.

Create a common metadata file

First, we will need to create a file that will contain all common metadata. I usually call that file HeadBase.astro and put it inside includes directory (to separate it from components, pages, and layouts).

It’s up to you what should belong to that “include”, but in general, stuff like SEO and social tags, favicon, common styles, and analytic scripts belong there.

If you’ll need some data from the page that will use that file, add necessary props to the HeadBase.

Here’s an example of such file. Note that it has two props, one of which is optional:

---
import './styles/global.css'

const { title, description = '' } = Astro.props;
---

<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />

<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
{description && <meta name="description" content={description} />}

<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
{description && <meta property="og:description" content={description} />}

<!-- Twitter -->
<meta property="twitter:card" content="summary" />
<meta property="twitter:title" content={title} />
{description && <meta property="twitter:description" content={description} />}

Use the file inside layouts and pages

Now that we created an include file, we can use it inside our pages and layouts:

---
import HeadBase from '../includes/HeadBase.astro';

const { content } = Astro.props;
const { title, description } = content;
---

<html>
	<head>
		<HeadBase {title} {description} />
	</head>

	<body>
		<div>Content</div>
	</body>
</html>

Update the file

The beauty of such pattern is that it enables us to update any part of the common metadata by updating our HeadBase file.

Let’s say we want to add another global stylesheet. We just need to add a new <link> at the bottom of the HeadBase.astro file. This will add that stylesheet to all pages.

Another option is adding common functionality to the pages. Let’s extend the include by adding a new optional url prop which will be used to generate social tags:

---
const { title, description = '', url = '' } = Astro.props;
---

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
{url && <meta property="og:url" content={url} />}
<meta property="og:title" content={title} />
{description && <meta property="og:description" content={description} />}

<!-- Twitter -->
<meta property="twitter:card" content="summary" />
{url && <meta property="twitter:url" content={url} />}
<meta property="twitter:title" content={title} />
{description && <meta property="twitter:description" content={description} />}

Now we can pass the new prop into one of the pages:

<html>
	<head>
		<HeadBase {title} {description} {url} />
	</head>
</html>

Since the prop is optional, we don’t need to update unaffected pages.