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
    
    	await 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>

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