EnglishNederlandsFrançais Toggle theme

Eleventy Baseline

Start building your site, skip the recurring setup work.
Table of Contents

Integrate with Eleventy Base Blog

This article is planned for revision, its content might be stale.

Add Baseline to the official eleventy-base-blog starter with as little disruption as the starter's existing wiring allows. The work splits into four pieces: bring Baseline in, line the starter's directories up with Baseline's contract, migrate the starter's hand-rolled <head> into Baseline's head pipeline, and confirm the output.

If you don't already have a project to integrate into, the simple site tutorial is the lighter starting point.

Prerequisites

  • You have eleventy-base-blog cloned and running (npm install, npm run dev works).
  • Eleventy 3.x; package.json has dev and build scripts.
  • settings.url will be set via env (URL) for production.

1) Install Baseline

npm install @apleasantview/eleventy-plugin-baseline

2) Wire Baseline into eleventy.config.js

In your existing config:

  • Remove the HtmlBasePlugin import and call. Baseline registers its own.

  • Import Baseline and the Baseline directory contract; add the plugin last so it sees the rest of your config:

    import baseline, { config as baselineConfig } from '@apleasantview/eleventy-plugin-baseline';
    import settings from './src/_data/settings.js';
    
    export default async function (eleventyConfig) {
    	// ...your existing config
    
    	eleventyConfig.addPlugin(baseline(settings));
    }
    
    export const config = baselineConfig; // spread to override if needed
  • Update passthroughCopy inputs to ./src/static/ and ./src/content/feed/pretty-atom-feed.xsl.
  • Remove watch targets for CSS or images if present (Baseline handles them).
  • Point CSS and JS bundle toFileDirectory to assets/css and assets/js respectively.
  • Set base to process.env.URL.

The full file is in the eleventy-base-blog fork.

3) Add a local .env for development

ELEVENTY_ENV="development"
URL="http://localhost:8080"

In CI and production, set URL to your live origin (e.g. https://www.example.com); Baseline uses it for canonical URLs and the sitemap.


4) Move Eleventy Base Blog folders under src/

Move the following folders to match Baseline's defaults:

  • _datasrc/_data
  • _includessrc/_includes
  • contentsrc/content
  • csssrc/assets/css
  • publicsrc/static

The last move has an asymmetry worth knowing upfront. Baseline's directory contract sets public: 'static', so the virtual key on eleventyConfig.directories is public but the folder on disk stays static/. The config-export reference covers the same split.

Update layouts and includes to resolve from src/, and content from src/content/. Delete sitemap.xml.njk in src/content/; Baseline's sitemap module handles it.

5) Add the required data file

src/_data/settings.js is the single source for site identity and head extras. The starter's _data/head.js is no longer in play once Baseline is wired; head extras move onto settings.head instead (step 7 walks through the migration):

export default {
	title: 'Eleventy Base Blog (Baseline Edition)',
	tagline: 'Baseline + Eleventy Base Blog',
	url: process.env.URL || 'http://localhost:8080/',
	defaultLanguage: 'en',
	noindex: false,

	head: {
		link: [{ rel: 'stylesheet', href: '/assets/css/index.css' }],
		script: [{ src: '/assets/js/index.js', defer: true }]
	}
};

6) Align assets

Make sure these exist (or create minimal stubs):

  • src/assets/css/index.css: import your base CSS or start empty.
  • src/assets/js/index.js: optional JS entry; can be empty.
  • src/assets/assets.11tydata.js to keep the assets directory out of collections:
    export default {
    	eleventyExcludeFromCollections: true
    };

7) Migrate the starter's head children

The starter's base.njk writes a <head> with three extras Baseline does not render for you: an Atom feed <link>, an inline <style> produced by eleventy-plugin-bundle, and a @zachleat/heading-anchors <script type="module">. With Baseline, you do not author your own <head>; you push these into the head pipeline. One pattern per extra, in the same order:

Atom feed link → settings.head.link[]. Site-wide static link. Add it to src/_data/settings.js:

head: {
	link: [
		{ rel: 'stylesheet', href: '/assets/css/index.css' },
		{
			rel: 'alternate',
			type: 'application/atom+xml',
			title: 'Eleventy Base Blog',
			href: '/feed/feed.xml'
		}
	];
}

@zachleat/heading-anchors script → settings.head.script[] plus an entry point. Re-export the module from a small entry under src/assets/js/heading-anchors/index.js:

import '@zachleat/heading-anchors';

Baseline's assets pipeline bundles it to dist/assets/js/heading-anchors/index.js. Reference it in head extras:

script: [
	{ src: '/assets/js/index.js', defer: true },
	{ src: '/assets/js/heading-anchors/index.js', type: 'module' }
];

Per-page bundled CSS → getBundle("css") style block. This is the non-trivial migration. The starter collects per-page CSS through eleventy-plugin-bundle and inlines the result. Baseline has no head-extras shape for inline <style>, so two approaches work depending on what the bundle is for:

  • If the styles are page-specific and small, keep the bundle plugin and inline them from the layout body (after <baseline-head>), with the small caveat that they sit outside the <head>. Most browsers handle this fine, but it's not strictly compliant.

  • If the styles are reusable, consolidate them into files under src/assets/css/ and inline with Baseline's inlinePostCSS filter, called inside an inlineable head fragment that the layout includes:

    {% set cssPath = _baseline.paths.assets ~ "css/post.css" %}
    {{ cssPath | inlinePostCSS | safe }}

    This belongs at the top of the layout body, not inside <baseline-head> (which Baseline replaces wholesale). The placeholder is your <head>; everything else goes in the body.

8) Add <baseline-head> to base.njk

The placeholder sits inside <html>, as a sibling of <body>. Baseline replaces it with the rendered <head> element at build time. Don't author your own <head> around it:

<!DOCTYPE html>
<html lang="en">
  <baseline-head></baseline-head>
  <body>
    ...
  </body>
</html>

9) Organise content and set permalinks.

Move the following files to src/content/pages/ and set their permalinks as needed:

  • 404.md, about.md, blog.njk, index.njk, tag-pages.njk, tags.njk

Example index.njk:

---js
const permalink = "/";
---

Add the permalink function in blog.11tydata.js:

export default {
	// Keep tags and layout keys.
	permalink: function ({ page }) {
		if (page.inputPath.includes('11tydata.js')) {
			return false;
		}
		return `/blog/${this.slugify(page.fileSlug)}/`;
	}
};

10) Use Baseline's inline-asset filters where it makes sense.

Replace direct CSS link-tags in src/_includes/layouts/post.njk and src/_includes/layouts/home.njk with the inline PostCSS filter. _baseline.paths.assets resolves to your assets input directory:

nunjucks {% set cssPath = _baseline.paths.assets ~ "css/message-box.css" %} {{ cssPath | inlinePostCSS | safe }}

11) Verify head, meta, and sitemap.

- Dev: npx rimraf dist/ && npm run dev - Build: npx rimraf dist/ && npm run build - Check a page's <head> for <title>, <meta name="description">, <link rel="canonical">, and (if multilingual) hreflang alternates. - Inspect dist/sitemap.xml for correct URLs (uses settings.url and pathPrefix if set).

12) Optional: navigator and debug filters.

- Set navigator: true in the plugin options if you want the navigator page (dev only). - Use the _inspect, _json, and _keys filters on a debug page to inspect data.

Next steps

Notes

  • Keep settings.url origin-only; use pathPrefix if you deploy under a subpath.
  • Baseline's assets pipeline uses src/assets/css/index.css and src/assets/js/index.js as the default entry points.
  • If you have other CSS or JS paths, either point settings.head.link[] and settings.head.script[] at the Baseline output paths or mirror the files into src/assets/.

See also