The text 'jshwlkr.info' written in marker.
jshwlkr.info

Joshua Walker


Eleventy Tips After a Redesign

In redesigning this website, and having maintained it in Eleventy for years now, I have some tips for using Eleventy.

Use the plugins

The plugin community exists. The official plugins especially will save you so much time. Dig aroung on GitHub. Ask for suggestions on Bluesky or Mastodon. The HTML base plugin will keep you sane during development.

Don't re-invent the wheel

While I understand people are allowed to like what they like and spend their time however they please, the amount of time people have spent reinventing the existing RSS and sitemap plugins is mind-boggling to me. There are so much more interesting and engagingly niche things to build in Eleventy[1].

Be careful of your paths

Watch out when building your templates. You can easily duplicate your images, if you're using the official image plugin, and end up unknowingly loading unnecessary bytes.

Everyone gets a data file

Use data files for everything. Every collection should get a default data file. Create a site data file and put whatever global variables you want.

My site.js gives me a timestamp and the environment variable, among other things.

const now = new Date();
const timeZone = "UTC";
const buildTime = new Intl.DateTimeFormat("en-US", {
  dateStyle: "full",
  timeStyle: "short",
  timeZone,
}).format(now);

export default {
  title: "jshwlkr.info",
  description: "The personal website of Joshua Walker.",
  url: "https://jshwlkr.info",
  author: "Joshua Walker",
  timestamp: {
    raw: now.toISOString(),
    formatted: `${buildTime} ${timeZone}`,
  },
  env: process.env.ELEVENTY_RUN_MODE,
};

My posts.11tydata.js sets the layout, the permalink, and uses zod to verify I've set various fields in the post correctly.

import { z } from "zod";
import { fromZodError } from 'zod-validation-error';

export default  {
  layout: "layouts/post.liquid",
  tags: "posts",
  permalink: function (data) {
    return (
      "/posts/" +
      data.page.date.getUTCFullYear() +
      "/" +
      `${this.slugify(data.title)}` +
      "/index.html"
    );
  },
    eleventyDataSchema: function(data) {
	    let result = z.object({
            description:  z.string().optional(),
            subtitle:     z.string().optional(),
            tags:         z.array(z.string()),
            title:        z.string(),
	    }).safeParse(data);

	    if( result.error ) {
	        throw fromZodError(result.error);
	    }
    }
};

Structure your projects

Whatever makes sense for you, but you especially want to notice if your build drops files outside your build directory unintentionally. Usually my projects take this form:

_assets
    images
_config
_content
    pages
    posts
_data
_includes
    layouts
    partials

Sometimes you should shave the yak

Certainly some people have said linting is unnecessary. I've found it useful, especially for avoiding those times when you spend hours debugging only to realize you fat-fingered a semicolon somewhere or overlooked a parenthesis. One of the easier ways of doing this is using Husky to hook various linters into a pre-commit hook (especially with lint-staged[2]). Linters could include ESLint, which at this particular moment will let you lint JavaScript, CSS, and Markdown. LiquidJS also has a linter. Additionally, you might want to drop a command in your package.json so you can manually check your build with something like Pa11y or spell check your code/content with CSpell. Linters are all over the place (dotenv, HTML, package.json) and many of them will also integrate with your editor. (I'm not even getting to code beautifiers like Prettier.)


  1. Not to mention the current labor crisis occurring in the open source community. All things being equal the community would be better served if people took that effort and spent it by helping maintain these plugins, or even Eleventy itself. ↩︎

  2. Prevent Bad Commits with Husky and lint-staged ↩︎

·