Head and NoIndex
Get a clean, correct <head> on every page with one placeholder and a settings file. By the end you'll have site-wide metadata, per-page head extras flowing through settings.head and front matter, and a proper canonical and robots story.
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
titleanddescription. - 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
- Baseline already installed and loaded, as in the simple site tutorial.
- Set
settings.urlto your production URL (or leave localhost for local testing). package.jsonwith"type": "module"and thedev/buildscripts:{ "name": "simple-baseline-site", "type": "module", "scripts": { "start": "rimraf dist/ && npx @11ty/eleventy --serve", "build": "rimraf dist/ && cross-env ELEVENTY_ENV=production npx @11ty/eleventy" } }
1) Ensure Baseline is loaded
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;
Two head options are worth knowing about:
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.
2) 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.URL || 'http://localhost:8080/',
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).
3) 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.
4) 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">`.
5) 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' }
Baseline computes the canonical URL from page.url and settings.url, stripping the query string and fragment. If settings.url is missing, no canonical is emitted (baseline warns at startup).
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.
6) 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, and the sitemap is skipped entirely.
Per-page: set noindex: true in front matter. The page renders a robots noindex tag and drops out of the sitemap.
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).
7) 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.xmlwhile dev runs. The page should appear unlessnoindexis true.
8) Production build
Change URL in .env to an absolute URL. On a deployed site that's your real production URL; for this exercise, any absolute URL (e.g. https://www.example.com/) works to verify the output. Then run:
npm run build
- Inspect
dist/head-demo/index.htmlfor the rendered head. - Confirm
dist/sitemap.xmlincludes or excludes the page in line withnoindex. - Reset
URLin.envto "http://localhost:8080/".
Next steps
- Add custom
head.link[],head.script[], orhead.meta[]entries through_data/settings.jsor per-page front matter. - For the full multilingual setup, see the multilingual site tutorial and the head module reference.