Build Awesome / 11ty Power Tools

Table of Contents

Base eleventy.config.js

The package includes a fully-configured Eleventy config file eleventy.config.js that you can symlink to your project to get:

Benefits of symlinking:

Installation as simple as:

npm install @anydigital/eleventy-blades
ln -s ./node_modules/@anydigital/eleventy-blades/src/eleventy.config.js

How it works

/* Plugins */
import { RenderPlugin } from "@11ty/eleventy";
import eleventyBladesPlugin from "@anydigital/eleventy-blades";
/* Dynamic plugins */
let eleventyNavigationPlugin;
try {
  eleventyNavigationPlugin = (await import("@11ty/eleventy-navigation")).default;
} catch (e) {
  // @11ty/eleventy-navigation not installed
}
let pluginTOC;
try {
  pluginTOC = (await import("@uncenter/eleventy-plugin-toc")).default;
} catch (e) {
  // @uncenter/eleventy-plugin-toc not installed
}
/* Libraries */
import markdownIt from "markdown-it";
/* Dynamic libraries */
let slugify;
try {
  slugify = (await import("@sindresorhus/slugify")).default;
} catch (e) {
  // @sindresorhus/slugify not installed
}
let markdownItAnchor;
try {
  markdownItAnchor = (await import("markdown-it-anchor")).default;
} catch (e) {
  // markdown-it-anchor not installed
}
let markdownItAttrs;
try {
  markdownItAttrs = (await import("markdown-it-attrs")).default;
} catch (e) {
  // markdown-it-attrs not installed
}
/* Data */
import yaml from "js-yaml";

/**
 * Eleventy Configuration
 * @param {Object} eleventyConfig - The Eleventy configuration object
 * @returns {Object} The Eleventy configuration object
 */
export default function (eleventyConfig) {
  const inputDir = eleventyConfig.directories.input;

  /* Jekyll parity */
  eleventyConfig.addPassthroughCopy("assets");
  eleventyConfig.addGlobalData("layout", "default");
  eleventyConfig.setLiquidOptions({ dynamicPartials: false }); // allows unquoted Jekyll-style includes
  eleventyConfig.addFilter("relative_url", (content) => content);

  /* Plugins */
  eleventyConfig.addPlugin(RenderPlugin);
  if (eleventyNavigationPlugin) eleventyConfig.addPlugin(eleventyNavigationPlugin);
  eleventyConfig.addPlugin(eleventyBladesPlugin, {
    mdAutoNl2br: true,
    mdAutoRawTags: true,
    autoLinkFavicons: true,
    siteData: true,
    filters: [
      "attr_set",
      "attr_includes",
      "merge",
      "remove_tag",
      "if",
      "attr_concat",
      "fetch",
      "section",
      "strip_tag",
      "unindent",
    ],
  });
  if (pluginTOC) {
    eleventyConfig.addPlugin(pluginTOC, {
      ignoredElements: ["sub", "[data-is-anchor]"],
      ul: true,
      wrapper: (toc) => `<div data-is-toc>${toc}</div>`,
    });
  }

  /* Libraries */
  let md = markdownIt({
    html: true,
    linkify: true,
  });
  if (markdownItAnchor) {
    md = md.use(markdownItAnchor, {
      slugify: slugify, // @TODO: TRICKS
      permalink: markdownItAnchor.permalink.ariaHidden({
        class: null,
        renderAttrs: () => ({ "data-is-anchor": true }),
      }),
    });
  }
  if (markdownItAttrs) md = md.use(markdownItAttrs);
  eleventyConfig.setLibrary("md", md);
  eleventyConfig.addFilter("markdownify", (content) => md.render(String(content ?? "")));

  /* Data */
  eleventyConfig.addDataExtension("yml", (contents) => yaml.load(contents));

  /* Build */
  eleventyConfig.addPassthroughCopy(
    {
      _public: ".",
      ...(inputDir !== "." && { [`${inputDir}/_public`]: "." }),
    },
    { expand: true }, // This follows/resolves symbolic links
  );

  /* Dev tools */
  // Follow symlinks in Chokidar used by 11ty to watch files
  eleventyConfig.setChokidarConfig({ followSymlinks: true });
}

Base 11ty npm scripts via npm workspace

This package provides a pre-configured do folder setup that helps organize your development workflow using npm workspaces. The do folder contains scripts for building and running your Eleventy project.

Installation:

  1. Install /anydigital/eleventy-blades to reuse pre-defined 11ty scripts from there:
npm install @anydigital/eleventy-blades
  1. Create a helper folder do to symlink the do/package.json within:
mkdir do
cd ./do
ln -s ../node_modules/@anydigital/eleventy-blades/src/do/package.json
  1. Finally register do folder as npm workspace in your root package.json:
{
  ...
  "workspaces": ["do"],
  "scripts": {
    "start": "npm -w do run start",
    "stage": "npm -w do run stage",
    "build": "npm -w do run build"
  },
  ...
}

Done! 🎉 Now you can run:

Living example: /anydigital/sveleven

Benefits:


Data helpers

Adds global site data to your Eleventy project, providing commonly needed values that can be accessed in all templates:

Variable Value
{{ site.year }} The current year as a number (e.g., 2026)
{{ site.prod }} Boolean indicating if running in production mode (true for eleventy build, false for eleventy serve)

How it works
/**
 * Add site.year and site.prod global data
 * - site.prod: Boolean indicating if running in production mode (build) vs development (serve)
 * - site.year: Sets the current year to be available in all templates as {{ site.year }}
 *
 * @param {Object} eleventyConfig - The Eleventy configuration object
 */
export function siteData(eleventyConfig) {
  eleventyConfig.addGlobalData("site.prod", () => process.env.ELEVENTY_RUN_MODE === "build");
  eleventyConfig.addGlobalData("site.year", () => new Date().getFullYear());
}

Appendix

Find and kill 11ty processes

ps aux | grep eleventy
pkill -f eleventy

You can even combine it with other processes hanging around:

ps aux | grep -E 'eleventy|tailwind|.bin/serve'
pkill -f tailwind
pkill -f .bin/serve