Table of Contents

Head and noindex

Get a clean, correct <head> on every page with one placeholder and a settings file. You write the placeholder, and Baseline writes everything that goes in it.

Hreflang is covered in the multilingual site tutorial. The placeholder is the only piece of HTML you write yourself.

The placeholder, <baseline-head>, is the element your layout puts on the page.

Baseline swaps it for a sorted, deduplicated <head> at transform-time, the stage of the build where Eleventy hands rendered HTML to plugins for last-mile mutation.

The data that fills it (title, description, canonical, links, scripts, hreflang) is gathered earlier, at cascade-time, when Eleventy is computing each page's data.

Two stages, one placeholder.


What you will build

  • A page rendered with proper title and description.
  • Canonical and robots tags written automatically.
  • Site-wide and per-page head extras through link[], script[], meta[].
  • Noindex controls at both site and page level.
  • A neatly organised <head>, thanks to Capo.js.

Prerequisites

settings.url set. Canonical URLs are the one thing on this page that needs a real origin.


Set the head options

Two options shape what the head module emits. Add them to the plugin call in eleventy.config.js:

import baseline, { config as baselineConfig } from '@apleasantview/eleventy-plugin-baseline';
import settings from './src/_data/settings.js';

/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
export default async function (eleventyConfig) {
	await eleventyConfig.addPlugin(
		baseline(settings, {
			head: {
				titleSeparator: ' | ',
				showGenerator: true
			}
		})
	);
}

export const config = baselineConfig;

titleSeparator controls how the page title and site title are joined. Defaults to (en-dash with surrounding spaces).

showGenerator adds a <meta name="generator"> tag. Off by default; flip it on if you want the credit visible in production.


Site-wide metadata

Update src/_data/settings.js. The fields below are the source of truth for what Baseline writes into every page's head:

export default {
	title: 'Baseline Head Demo',
	tagline: 'Head quickstart',
	url: process.env.BASELINE_URL,
	defaultLanguage: 'en',
	noindex: false,

	head: {
		link: [
			{ rel: 'stylesheet', href: '/assets/css/index.css' },
			{ rel: 'me', href: 'https://mastodon.social/@yourhandle' }
		],
		script: [{ src: '/assets/js/index.js', defer: true }],
		meta: [{ name: 'color-scheme', content: 'light dark' }]
	}
};

Each entry in link[], script[], and meta[] becomes an element on every page, alongside the defaults Baseline writes itself (charset, viewport, title, description, canonical, robots).


Layout with the placeholder

Create src/_includes/layouts/base.njk. Baseline replaces <baseline-head></baseline-head> with the rendered <head> at build time. It sits as a sibling of <body>, never inside a <head> of your own.

If the placeholder is missing, Baseline quietly skips the replacement and nothing is injected. Refer to the minimal layout in the simple site tutorial.


Page front matter

Create src/content/pages/head-demo.md:

---
title: 'Head Demo'
slug: 'head-demo'
description: 'A page showing Baseline head defaults.'
layout: 'layouts/base.njk'
---

This page uses Baseline's head defaults for canonical and robots. The title and description in the front matter become the rendered `<title>` and `<meta name="description">`.

Per-page head overrides

A page's own front matter can extend or override the site-wide head. Add this block to src/content/pages/head-demo.md:

head:
  meta:
    - { name: 'description', content: 'Override description for this page' }
  link:
    - { rel: 'author', href: 'https://yourdomain.com' }

The canonical is written for you, from the page's own URL and settings.url. The head module has the exact rules, including what the query string does and what happens when settings.url is missing.

To override the canonical per page, set a top-level canonical field in front matter. See the SEO front-matter table in Site settings.

Open Graph and Twitter card tags are emitted automatically from settings.seo and per-page front matter; you no longer hand-roll them as head.meta[] entries.


Noindex (site-wide and per-page)

Three knobs, in widening order of scope:

Site-wide: set noindex: true in _data/settings.js. Every page gets a robots noindex tag, canonical links are dropped, and the sitemap still renders but lists nothing.

Per-page: set noindex: true in front matter, or noindex: true under seo: if you keep the presentation keys together. The page renders a robots noindex tag and drops out of the sitemap. The seo: spelling wins where both are set, which is how a page opts back in against a noindex: true it inherited from a directory data file.

Sitemap-only: set sitemap: { ignore: true } in front matter. The page renders normally; only the sitemap entry is removed.

The relationship is deliberate: noindex is the broader signal (don't index, don't list), sitemap.ignore is the narrow one (just don't list). The sitemap module lists the same knobs from the sitemap's side, plus the fourth one that only affects collections.


Run and inspect

npm start
  • Open /head-demo/ (default http://localhost:8080/head-demo/).
  • View source. Confirm the placeholder is gone, the canonical (or your override) is in, and the defaults for description and robots are rendered.
  • Check dist/sitemap.xml while dev runs. The page should appear unless noindex is true.

Production build

npm run build
  • Inspect dist/head-demo/index.html for the rendered head.
  • Confirm dist/sitemap.xml includes or excludes the page in line with noindex.

The canonical points at whatever origin settings.url resolved to for this build. Getting that right per environment is its own subject: see deployment checks.


Next steps

  • Add custom head.link[], head.script[], or head.meta[] entries through _data/settings.js or per-page front matter.

See also