Eleventy vs Jekyll for Markdown-Heavy Blogs

For a blog with thousands of Markdown files, the choice between Eleventy and Jekyll comes down to four things: how the Markdown parser is configured, how the data cascade resolves, how reliable incremental builds are, and how fast a full production build runs. This is a focused comparison; for plugin and dependency specifics see the Jekyll Plugin Ecosystem, and for the wider framework decision, Choosing the Right Static Site Generator for Production.

Prerequisites

  • A Markdown corpus large enough that build time matters — roughly a thousand files or more.
  • Node 18+ available for Eleventy, and Ruby 3.3+ with Bundler for Jekyll.
  • A way to time production builds repeatably (hyperfine is ideal) so the comparison is not a single noisy run.
Eleventy versus Jekyll on a markdown-heavy blog A side-by-side comparison of Eleventy on Node with markdown-it and Jekyll on Ruby with Kramdown and Liquid, showing parser, data model, incremental reliability, and a cold build time of about 19 seconds for Eleventy versus 58 seconds for Jekyll on a 5,000-post corpus. 5,000-post corpus, same content, both engines Eleventy · Node Parser: markdown-it Data: cascade + JS data files Incremental: dependable (local) Extend: addFilter, JS plugins cold full build ~19s Jekyll · Ruby Parser: Kramdown + Rouge Data: _data + front-matter defaults Incremental: misses deps Extend: Liquid filters, Ruby gems cold full build ~58s
On the same 5,000-post corpus, Eleventy's Node/markdown-it pipeline built cold in ~19s against Jekyll's ~58s; the qualitative differences (data model, incremental reliability, extension language) often matter as much as the seconds.

Markdown Parsing & Extensibility

Eleventy uses markdown-it, whose token-stream design makes custom syntax easy to add. Jekyll uses Kramdown, which is solid but configured differently. The practical difference shows up when you want custom blocks (callouts, admonitions).

Eleventy — register markdown-it plugins directly:

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

module.exports = function (eleventyConfig) {
  const md = markdownIt({ html: true, linkify: true, typographer: true })
    .use(require("markdown-it-container"), "note"); // enables ::: note blocks
  eleventyConfig.setLibrary("md", md);
};

Jekyll — configure Kramdown for GFM plus Rouge highlighting:

# _config.yml
kramdown:
  input: GFM
  syntax_highlighter: rouge
  syntax_highlighter_opts:
    block:
      line_numbers: true

Both render GFM tables once configured; the difference is that Eleventy's extension model is JavaScript you already know, while Kramdown extensions live in Ruby.

Data Cascade & Frontmatter

Jekyll merges _data/ globally and applies front matter defaults from _config.yml. Eleventy resolves data top-down through its cascade, with eleventyComputed for per-item overrides and addGlobalData for site-wide values. Eleventy's data files can be plain JavaScript (require() real modules), which avoids the awkwardness of expressing complex structures in YAML. In either tool, keep frontmatter shallow — deeply nested YAML is slow to parse and easy to get wrong; move complex data into data files.

Eleventy data cascade resolution order for a single post Four data layers merge for one post, from lowest to highest priority: global data from the _data folder, then the directory data file posts.11tydata.js, then the post's own front matter, then eleventyComputed. Later layers override earlier ones, so the resolved post inherits the layout and tags from the directory, its title from front matter, and a derived permalink from eleventyComputed. How one post's data resolves — later layers win low high priority 1 · Global data — _data/, addGlobalData site-wide defaults: site.title, env, nav 2 · Directory data — posts.11tydata.js layout: post.njk · tags: [post] 3 · Template front matter — the post's YAML title, date, any custom fields 4 · eleventyComputed — derived per item permalink, readingTime (can override 1–3) merge Resolved data · one post site.title from global layout from directory tags from directory title from front matter permalink computed A conflicting key from a lower layer is overwritten.
The cascade merges four layers per post from lowest to highest priority — global data, the directory's posts.11tydata.js, the post's front matter, then eleventyComputed — so directory defaults supply layout and tags while a computed permalink can override anything below it.

For a large blog the cascade earns its keep on directory-level defaults. Drop a posts.11tydata.js beside your Markdown to give every post in that folder a layout, tag, and computed permalink without repeating it in a thousand files:

// src/posts/posts.11tydata.js
module.exports = {
  layout: "post.njk",
  tags: ["post"],
  eleventyComputed: {
    // derive a stable, collision-free URL from the source path + date
    permalink: (data) =>
      `/blog/${data.page.date.getFullYear()}/${data.page.fileSlug}/`,
  },
};

The Jekyll equivalent lives in _config.yml as a defaults scope keyed by path, which works but cannot compute a value from the page itself — you get static defaults, not derived ones. That gap is why per-post permalink logic and reading-time fields are simpler to express in Eleventy on a Markdown-heavy corpus.

Build Performance & Incremental Reliability

This is where they diverge most. Jekyll's --incremental is explicitly a development convenience and is known to miss dependencies — a changed layout or _data/ file may not regenerate every page that depends on it, so production deploys should use full builds. Eleventy's --incremental is more dependable but is also intended for local iteration. Define explicit watch targets so data changes trigger the rebuilds you expect:

// eleventy.config.js
module.exports = function (eleventyConfig) {
  eleventyConfig.setServerOptions({ liveReload: true, domDiff: true });
  eleventyConfig.addWatchTarget("./src/_data/");
};

For CI, run full builds in production mode for both (bundle exec jekyll build and npx @11ty/eleventy), and reserve incremental flags for local authoring.

What each generator gives you out of the box Two panels. Jekyll provides Kramdown, Liquid, collections, a gem plugin ecosystem and a hosted build on GitHub Pages. Eleventy provides markdown-it, a choice of template languages, the data cascade, npm packages and incremental builds, with no hosted build. What each generator gives you out of the box Jekyll Kramdown Markdown, Liquid templates collections defined in config Ruby gem plugins, allowlisted on Pages a hosted build that needs no pipeline no incremental rebuilds Eleventy markdown-it with plugin hooks pick your template language the data cascade for defaults any npm package, no allowlist --incremental for the edit loop Both read the same Markdown files; a corpus can usually be moved between them with a conversion script.
The deciding factor is rarely the Markdown parser — it is whether you want a hosted build or a pipeline you control.

Measured Impact

The numbers below come from a single 5,000-post Markdown corpus — same files, same frontmatter, same image references — built on a fixed 2-vCPU container and timed with hyperfine --warmup 1 --runs 10:

MetricEleventyJekyll
Cold full build (mean)19.1s ± 0.6s57.8s ± 1.7s
Warm full build (mean)18.4s ± 0.5s55.2s ± 1.4s
Local incremental edit (1 post)~0.4s~1.1s (misses some deps)
Peak RSS (cold build)~640 MB~810 MB

Two things stand out. The cold-build gap is large and mostly reflects the Node/markdown-it pipeline against Ruby/Kramdown plus Liquid. And the full warm build barely differs from cold for either tool, because neither does meaningful incremental work for production — the warm win is in local editing, where Eleventy's incremental mode is both faster and more dependable. For a rigorous way to produce comparable numbers yourself, follow How to Benchmark Hugo vs Astro Build Speeds — the same controlled harness applies to any pair of generators.

Plugin & Filter Model

Custom filters are simple in both, just in different languages: Jekyll registers them through a Liquid plugin (Liquid::Template.register_filter), Eleventy with eleventyConfig.addFilter. The maintenance trade-off matters more than the API: a JavaScript filter is a quick fix-and-redeploy, whereas an abandoned Ruby gem with native extensions can block a build until someone patches it — a recurring theme in the Jekyll ecosystem. If that maintenance risk is what is pushing you off Jekyll, the concrete swap list is in Replacing Jekyll Plugins When Migrating to Eleventy.

Pitfalls & Rollback

  • Deeply nested Liquid includes: very deep recursive include chains can raise Liquid::StackLevelError. Flatten the include hierarchy, or move layout logic into Eleventy's computed data.
  • Duplicate permalinks: when files share a name across folders, a fileSlug-only permalink collides. Use a permalink that includes the path or date, or set eleventyComputed.permalink for deterministic routing.
  • Kramdown TOC vs raw HTML: Kramdown's {:toc} can choke on embedded HTML or unescaped Liquid. Wrap literal template syntax in {% raw %} and keep custom HTML out of the TOC region.
  • Rollback: keep both engines building in parallel during a migration. Because the Markdown source is shared, you can revert to the Jekyll build by switching the deploy command back until the Eleventy output matches.

Which One a Blog Should Actually Choose

For a Markdown-heavy blog the deciding questions are narrow, because both generators handle posts, tags and pagination competently.

Choose Jekyll when the site should build without a pipeline. The hosted build on GitHub Pages is genuinely valuable for a personal or small team blog: push a Markdown file and the site updates, with no workflow file, no Node version to pin and nothing to maintain. Accept the plugin allowlist as the price.

Choose Eleventy when the edit loop matters or the plugin list does not fit the allowlist. Incremental rebuilds turn a multi-second save into an instant one on a corpus of any size, and any npm package is available without asking permission. The price is a build pipeline you own, which for a blog is one workflow file.

The migration between them is unusually cheap in either direction, because the content is the same Markdown with the same front matter. That is worth knowing at the moment of choosing: this is not a decision that locks a blog in for years, and treating it as one tends to delay it far longer than it deserves.

Conclusion

Choose Eleventy if you want JavaScript-native extensibility, a flexible data cascade, more dependable incremental builds, and the faster cold build measured above; choose Jekyll if you value its long-stable conventions and Ruby ecosystem. For a Markdown-heavy blog at scale, the deciding factors are usually incremental-build reliability and how painful your custom-filter language is to maintain — both of which lean Eleventy. Either way, settle the dependency and CI discipline first by reading the Jekyll Plugin Ecosystem.

FAQ

Does Eleventy support incremental builds for large blogs?

Yes, via --incremental with configured watch targets, but treat it as a local-development accelerator and run full builds for production deploys. Eleventy's incremental mode is more dependable than Jekyll's, though it is still intended for local iteration.

How should I handle complex frontmatter in Jekyll?

Keep frontmatter shallow and move complex or nested structures into _data/ files in YAML or JSON, which are easier to validate than deeply nested inline YAML. The same advice applies to Eleventy, where data files can be plain JavaScript modules.

Which renders Markdown tables better for documentation?

Both do once configured. Eleventy's markdown-it enables GFM tables with minimal setup, while Jekyll needs kramdown.input: GFM. The difference is configuration effort, not output quality.

Is Eleventy faster than Jekyll on a large Markdown corpus?

Generally yes on cold builds, because Eleventy runs on Node with markdown-it while Jekyll runs Ruby with Kramdown and Liquid. On a 5,000-post corpus Eleventy built in about 19 seconds cold versus roughly 58 seconds for Jekyll, though exact numbers depend on template complexity.

Which is easier to extend for a markdown-heavy blog?

Eleventy, for most JavaScript teams. Its markdown-it plugins and addFilter API are JavaScript you likely already know, whereas Kramdown extensions and Liquid filters live in Ruby and can be blocked by an unmaintained native gem.