Build Awesome / 11ty Power Tools
Table of ContentsBase eleventy.config.js
The package includes a fully-configured Eleventy config file eleventy.config.js that you can symlink to your project to get:
- All eleventy-blades plugins enabled
- Eleventy Navigation plugin
- Table of Contents plugin (conditionally loaded if installed)
- Markdown-it with anchors and attributes
- YAML data support
- CLI input directory support
- Symlink support for development
- and more
Benefits of symlinking:
- Always up-to-date: Configuration automatically updates when you upgrade the package
- Less maintenance: No need to manually sync configuration changes
- Quick setup: Get started immediately with best-practice configurations
- Easy customization: Override specific settings by creating your own config that imports from the symlinked version
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:
- Install
/anydigital/eleventy-blades to reuse pre-defined 11ty scripts from there:
npm install @anydigital/eleventy-blades
- Create a helper folder
doto symlink thedo/package.jsonwithin:
mkdir do
cd ./do
ln -s ../node_modules/@anydigital/eleventy-blades/src/do/package.json
- Finally register
dofolder as npm workspace in your rootpackage.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:
npm startto start 11ty dev server with live reload and Tailwind watch modenpm run stageto build and serve production-like site locallynpm run buildto finally build the site for production- all available scripts:
/anydigital/eleventy-blades/blob/main/src/do/package.json
Living example: /anydigital/sveleven
Benefits:
- Clean separation: Keep build scripts separate from project configuration
- Reusable workflows: Update scripts by upgrading the package
- Workspace isolation: Scripts run in their own workspace context
- Easy maintenance: No need to manually maintain build scripts
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