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 (
hyperfineis ideal) so the comparison is not a single noisy run.
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.
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.
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:
| Metric | Eleventy | Jekyll |
|---|---|---|
| Cold full build (mean) | 19.1s ± 0.6s | 57.8s ± 1.7s |
| Warm full build (mean) | 18.4s ± 0.5s | 55.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
includechains can raiseLiquid::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 seteleventyComputed.permalinkfor 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.
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.
Related
- Parent: Jekyll Plugin Ecosystem — dependency and CI discipline for Jekyll.
- Replacing Jekyll Plugins When Migrating to Eleventy — the plugin-to-equivalent mapping for a move.
- How to Benchmark Hugo vs Astro Build Speeds — produce comparable build numbers yourself.
- Astro vs Eleventy for Documentation Sites — Eleventy weighed against Astro for docs.