Advanced Features & Optimization

Master advanced techniques for building sophisticated sites with 11ty and Standard Framework.

Performance Optimization

Build Speed Optimization

Reduce build times for large sites:

// eleventy.config.js
import Standard from "@zefish/standard";

export default function (eleventyConfig) {
  eleventyConfig.addPlugin(Standard);

  // Incremental builds during development
  eleventyConfig.setIncrementalWatch(true);

  // Cache bust for long build times
  eleventyConfig.setIncrementalWatch(["./content/**/*"]);

  // Passthrough copy only needed files
  eleventyConfig.addPassthroughCopy({
    "./src/images/": "/images/",
    "./src/fonts/": "/fonts/"
  });

  // Ignore node_modules and other large dirs
  eleventyConfig.setInputDirectory("content");
  eleventyConfig.setIncludesDirectory("_includes");
  eleventyConfig.setOutputDirectory("_site");

  return {
    dir: {
      input: "content",
      output: "_site",
      includes: "_includes"
    }
  };
}

Bundle Size Optimization

// Use production CSS only when needed
export default function (eleventyConfig) {
  eleventyConfig.addPlugin(Standard, {
    // Minify CSS/JS in production
    minify: process.env.NODE_ENV === "production",

    // Use CDN for production
    useCDN: process.env.NODE_ENV === "production",

    // Disable unused features
    features: {
      backlinks: true,
      encryption: true,
      filters: true,
      markdown: true
    }
  });

  return { /* ... */ };
}

Lazy Loading Images

<!-- In templates -->
<img
  src="{{ image }}"
  alt="{{ alt }}"
  loading="lazy"
  decoding="async"
  width="400"
  height="300"
>

CSS Critical Path

Load critical CSS inline:

<!-- In base layout -->
<head>
  {% set criticalCSS %}
    {% include "css/critical.css" %}
  {% endset %}
  <style>{{ criticalCSS }}</style>

  <!-- Load rest asynchronously -->
  <link rel="preload" href="/css/standard.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/css/standard.min.css"></noscript>
</head>

Custom Layouts & Templates

Nested Layout Inheritance

Create flexible layout hierarchies:

<!-- _includes/layouts/base.njk -->
<!DOCTYPE html>
<html>
<head>
  {% standardAssets %}
</head>
<body>
  {{ content | safe }}
</body>
</html>
<!-- _includes/layouts/main.njk -->
---
layout: layouts/base.njk
---

<main class="max-w-4xl mx-auto px-4">
  {{ content | safe }}
</main>
<!-- _includes/layouts/blog-post.njk -->
---
layout: layouts/main.njk
---

<article class="rhythm-block">
  <header>
    <h1>{{ title }}</h1>
    <time datetime="{{ date | dateFilter }}">{{ date | dateFilter('long') }}</time>
  </header>

  {{ content | safe }}

  <footer>
    {% if previousPost %}
      <a href="{{ previousPost.url }}">Previous</a>
    {% endif %}
    {% if nextPost %}
      <a href="{{ nextPost.url }}">Next</a>
    {% endif %}
  </footer>
</article>

Use in markdown:

---
layout: layouts/blog-post.njk
title: My Article
date: 2024-10-21
---

# Content here

Parametric Layouts

Reuse layouts with different parameters:

// eleventy.config.js
eleventyConfig.addNunjucksFilter("applyLayout", (content, layoutName, data) => {
  // Apply layout dynamically
});
<!-- _includes/layouts/card.njk -->
<div class="card {{ cardClass }}">
  <div class="card-header">
    {% if cardIcon %}
      <span class="icon">{{ cardIcon }}</span>
    {% endif %}
    <h3>{{ cardTitle }}</h3>
  </div>
  <div class="card-body">
    {{ content | safe }}
  </div>
  {% if cardFooter %}
    <div class="card-footer">{{ cardFooter }}</div>
  {% endif %}
</div>

Use with variables:

---
layout: layouts/card.njk
cardTitle: "Feature Highlight"
cardIcon: "⭐"
cardClass: "featured"
---

Content inside card.

Global Data & Configuration

Shared Data Structure

Create global data accessible everywhere:

// eleventy.config.js
export default function (eleventyConfig) {
  // Single data object
  eleventyConfig.addGlobalData("site", {
    name: "My Site",
    url: "https://example.com",
    description: "Amazing site built with Standard",
    author: "Your Name",
    socialLinks: {
      twitter: "https://twitter.com/yourname",
      github: "https://github.com/yourname"
    }
  });

  // Version data
  const packageJson = require("./package.json");
  eleventyConfig.addGlobalData("version", packageJson.version);

  // Build time
  eleventyConfig.addGlobalData("buildTime", new Date());

  return { /* ... */ };
}

Access in templates:

<footer>
  <p>&copy; {{ site.name }} - v{{ version }}</p>
  <a href="{{ site.socialLinks.twitter }}">Twitter</a>
  <a href="{{ site.socialLinks.github }}">GitHub</a>
  <p>Built {{ buildTime | dateFilter('long') }}</p>
</footer>

Dynamic Data Files

Load data from external sources:

// _data/posts.js
export default async function() {
  // Fetch from API
  const response = await fetch("https://api.example.com/posts");
  const posts = await response.json();
  return posts;
}

// Or load from filesystem
import fs from "fs";
import yaml from "js-yaml";

export default function() {
  const file = fs.readFileSync("./data/config.yml", "utf8");
  return yaml.load(file);
}

Use in templates:

<!-- Use data from _data/posts.js -->
{% for post in posts %}
  <a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}

Advanced Collections

Multi-Dimensional Collections

// Create nested collections
eleventyConfig.addCollection("postsByYear", (collection) => {
  const posts = collection.getFilteredByGlob("content/posts/**/*.md");

  // Group by year
  const grouped = {};
  posts.forEach(post => {
    const year = post.date.getFullYear();
    if (!grouped[year]) grouped[year] = [];
    grouped[year].push(post);
  });

  return grouped;
});

// Create tag index
eleventyConfig.addCollection("tagIndex", (collection) => {
  const tags = new Set();
  collection.getAll().forEach(item => {
    (item.data.tags || []).forEach(tag => tags.add(tag));
  });
  return Array.from(tags).sort();
});

Use in templates:

<!-- Posts organized by year -->
{% for year, posts in postsByYear %}
  <h2>{{ year }}</h2>
  <ul>
    {% for post in posts %}
      <li><a href="{{ post.url }}">{{ post.data.title }}</a></li>
    {% endfor %}
  </ul>
{% endfor %}

<!-- Tag index -->
<div class="tags">
  {% for tag in tagIndex %}
    <a href="/tags/{{ tag | slugify }}/">{{ tag }}</a>
  {% endfor %}
</div>

Computed Collections

// Create virtual collections
eleventyConfig.addCollection("popular", (collection) => {
  return collection
    .getFilteredByGlob("content/**/*.md")
    .filter(item => item.data.featured === true)
    .sort((a, b) => b.data.views - a.data.views)
    .slice(0, 10);
});

eleventyConfig.addCollection("recent", (collection) => {
  return collection
    .getFilteredByGlob("content/**/*.md")
    .sort((a, b) => b.date - a.date)
    .slice(0, 5);
});

Custom Shortcodes

Reusable Components

// eleventy.config.js

// Simple shortcode
eleventyConfig.addNunjucksShortcode("highlight", (text) => {
  return `<mark class="highlight">${text}</mark>`;
});

// Shortcode with attributes
eleventyConfig.addNunjucksShortcode("callout", (content, type = "info") => {
  return `<div class="callout callout-${type}">${content}</div>`;
});

// Async shortcode
eleventyConfig.addNunjucksAsyncShortcode("image", async (src, alt) => {
  const imageSize = await getImageDimensions(src);
  return `
    <figure>
      <img src="${src}" alt="${alt}" width="${imageSize.width}" height="${imageSize.height}" loading="lazy">
      <figcaption>${alt}</figcaption>
    </figure>
  `;
});

// Paired shortcode
eleventyConfig.addPairedNunjucksShortcode("card", (content, title) => {
  return `
    <div class="card">
      <h3 class="card-title">${title}</h3>
      <div class="card-content">${content}</div>
    </div>
  `;
});

Use in templates:

<!-- Simple shortcode -->
This is {% highlight "important" %}

<!-- With attributes -->
{% callout "Warning message", "warning" %}

<!-- Async shortcode -->
{% image "/images/photo.jpg", "My photo" %}

<!-- Paired shortcode -->
{% card "Card Title" %}
  Card content goes here
{% endcard %}

Extending with Plugins

Create Custom Plugins

// my-plugin.js
export default function(eleventyConfig, options = {}) {
  const defaults = {
    features: ["feature1", "feature2"],
    debug: false
  };

  const config = { ...defaults, ...options };

  if (config.debug) {
    console.log("Custom plugin loaded with:", config);
  }

  // Add filters
  eleventyConfig.addFilter("myCustomFilter", (value) => {
    return value.toUpperCase();
  });

  // Add shortcodes
  eleventyConfig.addNunjucksShortcode("myShortcode", (text) => {
    return `<custom>${text}</custom>`;
  });

  // Add collections
  eleventyConfig.addCollection("myCollection", (collection) => {
    return collection.getAll().slice(0, 5);
  });

  // Add passthrough copy
  eleventyConfig.addPassthroughCopy("./src/custom-assets/");
}

Use the plugin:

// eleventy.config.js
import MyPlugin from "./my-plugin.js";

export default function (eleventyConfig) {
  eleventyConfig.addPlugin(MyPlugin, {
    features: ["feature1"],
    debug: true
  });

  return { /* ... */ };
}

Content Preprocessing

Transform Content

// eleventy.config.js

// Add transform to add reading time
eleventyConfig.addTransform("readingTime", (content, outputPath) => {
  if (outputPath?.endsWith(".html")) {
    const wordsPerMinute = 200;
    const wordCount = content.split(/\s+/).length;
    const readingTime = Math.ceil(wordCount / wordsPerMinute);

    // Inject reading time meta tag
    return content.replace(
      "</head>",
      `<meta name="reading-time" content="${readingTime} min">\n</head>`
    );
  }
  return content;
});

// Add transform to optimize images
eleventyConfig.addTransform("optimizeImages", (content, outputPath) => {
  if (outputPath?.endsWith(".html")) {
    // Add lazy loading to images
    return content.replace(
      /<img /g,
      '<img loading="lazy" decoding="async" '
    );
  }
  return content;
});

// Add transform to minify HTML in production
eleventyConfig.addTransform("htmlMinifier", (content, outputPath) => {
  if (process.env.NODE_ENV === "production" && outputPath?.endsWith(".html")) {
    const htmlmin = require("html-minifier");
    return htmlmin.minify(content, {
      useShortDoctype: true,
      removeComments: true,
      collapseWhitespace: true
    });
  }
  return content;
});

Common Patterns

Breadcrumb Navigation

// eleventy.config.js
eleventyConfig.addFilter("breadcrumbs", function(page) {
  const path = page.url.split("/").filter(Boolean);
  const crumbs = [];

  let url = "/";
  crumbs.push({ title: "Home", url: "/" });

  path.forEach(segment => {
    url += segment + "/";
    crumbs.push({
      title: segment.charAt(0).toUpperCase() + segment.slice(1),
      url: url
    });
  });

  return crumbs;
});

Use:

<nav aria-label="breadcrumb">
  <ol class="breadcrumbs">
    {% for crumb in page | breadcrumbs %}
      <li>
        {% if loop.last %}
          <span>{{ crumb.title }}</span>
        {% else %}
          <a href="{{ crumb.url }}">{{ crumb.title }}</a>
        {% endif %}
      </li>
    {% endfor %}
  </ol>
</nav>

Table of Contents

// eleventy.config.js
const markdownIt = require("markdown-it");

eleventyConfig.addFilter("tableOfContents", (content) => {
  const headings = [];
  const regex = /<h([2-3]) id="([^"]+)">([^<]+)<\/h[2-3]>/g;

  let match;
  while ((match = regex.exec(content)) !== null) {
    headings.push({
      level: parseInt(match[1]),
      id: match[2],
      text: match[3]
    });
  }

  return headings;
});

Generate TOC:

<!-- In markdown with front matter -->
{% set toc = content | tableOfContents %}

<aside class="toc">
  <h3>Table of Contents</h3>
  <ul>
    {% for heading in toc %}
      <li class="level-{{ heading.level }}">
        <a href="#{{ heading.id }}">{{ heading.text }}</a>
      </li>
    {% endfor %}
  </ul>
</aside>

{{ content | safe }}

Related Content

// eleventy.config.js
eleventyConfig.addFilter("relatedPosts", function(currentPage, collection, maxCount = 3) {
  return collection
    .getFilteredByGlob("content/posts/**/*.md")
    .filter(post => post.url !== currentPage.url)
    .filter(post => {
      const currentTags = currentPage.data.tags || [];
      const postTags = post.data.tags || [];
      return currentTags.some(tag => postTags.includes(tag));
    })
    .sort((a, b) => b.date - a.date)
    .slice(0, maxCount);
});

Deployment Optimization

Environment-Specific Configuration

// eleventy.config.js
const isDev = process.env.NODE_ENV === "development";
const isProd = process.env.NODE_ENV === "production";

export default function (eleventyConfig) {
  eleventyConfig.addPlugin(Standard, {
    // Disable features in dev to speed up builds
    backlinks: isProd,
    encryption: isProd,

    // Use CDN in production only
    useCDN: isProd,

    // Minify in production
    minify: isProd
  });

  // Dev-only debugging
  if (isDev) {
    eleventyConfig.addFilter("debug", (value) => {
      console.log(value);
      return value;
    });
  }

  return { /* ... */ };
}

Build Scripts

{
  "scripts": {
    "start": "NODE_ENV=development npx @11ty/eleventy --serve",
    "build": "NODE_ENV=production npx @11ty/eleventy",
    "debug": "NODE_ENV=development npx @11ty/eleventy --profile",
    "clean": "rm -rf _site",
    "prebuild": "npm run clean"
  }
}

Troubleshooting Advanced Issues

Build Performance Issues

# Profile build to find bottlenecks
npx @11ty/eleventy --profile

# Check which plugins are slowest
npx @11ty/eleventy --dryrun

Collection Errors

// Debug collections
eleventyConfig.addCollection("debug", (collection) => {
  console.log("Total items:", collection.getAll().length);
  console.log("Markdown items:", collection.getFilteredByGlob("content/**/*.md").length);
  return [];
});

Template Errors

<!-- Debug variables -->
{{ variable | dump }}

<!-- Safe property access -->
{{ (page.data.author or "Unknown") | upper }}

<!-- Conditional debugging -->
{% if debug %}
  <pre>{{ page | dump }}</pre>
{% endif %}

See Also


Build powerful, optimized sites with advanced 11ty techniques. You’ve mastered the Standard Framework! 🚀