Eleventy vs Jekyll for markdown-heavy blogs
Diagnostic comparison of Eleventy and Jekyll for repositories exceeding 1,000 markdown files. Evaluates AST traversal overhead, data cascade resolution, and incremental build reliability. Contextualizes plugin dependency management within the broader Choosing the Right Static Site Generator for Production framework. Addresses Ruby gem versus npm ecosystem trade-offs for technical documentation teams.
Markdown Parser Architecture & Extensibility
Kramdown relies on regex-based AST conversion. markdown-it utilizes token-stream parsing. This architectural difference dictates custom syntax injection behavior. AST traversal performance degrades when nested template includes exceed three levels.
Eleventy: Token-Stream Configuration
Eleventy delegates markdown processing to markdown-it. This enables zero-config GFM table rendering. Register custom admonition blocks directly in the configuration file.
// eleventy.config.js
module.exports = function(eleventyConfig) {
const markdownIt = require('markdown-it');
const md = new markdownIt({
html: true,
linkify: true,
typographer: true
});
md.use(require('markdown-it-container'), 'note');
eleventyConfig.setLibrary('md', md);
};
This setup registers the markdown-it-container plugin. It parses ::: note syntax without rigid block constraints. Bypasses legacy parser limitations for technical docs.
Jekyll: Kramdown & Rouge Integration
Jekyll defaults to Kramdown. GitHub Flavored Markdown tables require explicit configuration. Delegate syntax highlighting to Rouge for consistent output.
# _config.yml
kramdown:
input: GFM
syntax_highlighter: rouge
syntax_highlighter_opts:
css_class: highlight
span:
line_numbers: false
block:
line_numbers: true
start_line: 1
Forces Kramdown to parse GFM tables correctly. Prevents missing code block classes during static asset generation. Aligns with standard documentation styling requirements.
Data Cascade & Frontmatter Processing
Jekyll merges _data/ globally before template rendering. Eleventy resolves data top-down with explicit eleventyComputed overrides. SafeYAML restrictions in Jekyll frequently block complex nested objects. Eleventy bypasses this using native JavaScript require().
Pagination and collection filtering require different mapping strategies. Jekyll uses defaults in _config.yml. Eleventy uses addGlobalData in eleventy.config.js. Avoid deep YAML nesting to prevent serialization bottlenecks.
Build Performance & Incremental Compilation
Jekyll’s --incremental flag fails on layout changes. It also breaks when _data/ files modify. Eleventy tracks file hashes but requires explicit --incremental execution. Watch mode memory leaks occur when repositories exceed 2,000 markdown files.
Cache Invalidation & Watch Targets
Resolve Eleventy’s default behavior of ignoring nested data mutations. Explicitly define watch targets to prevent full rebuilds.
// eleventy.config.js
module.exports = function(eleventyConfig) {
eleventyConfig.setServerOptions({
liveReload: true,
domDiff: true,
port: 8080
});
eleventyConfig.addWatchTarget('./src/_data/');
};
Execute bundle exec jekyll build --incremental for Jekyll validation. Run npx @11ty/eleventy --incremental --dryrun for Eleventy. Monitor memory allocation using --quiet. Suppress verbose console output during CI/CD execution.
Plugin Ecosystem & Dependency Management
Ruby gem conflicts via Bundler introduce native C extension compilation. npm dependency trees resolve faster. They require strict package-lock.json version pinning. Custom filter implementation differs significantly between ecosystems.
Jekyll uses Liquid::Template.register_filter. Eleventy uses eleventyConfig.addFilter. Evaluate plugin stability and security patching velocity carefully. The Jekyll Plugin Ecosystem in 2024 highlights how abandoned gems frequently break CI/CD pipelines. Pure JavaScript plugins offer faster hotfix deployment cycles.
Common Pitfalls & Fast Resolutions
Liquid Include Recursion Limits
Liquid caps recursive include tags at 100 calls. Deep documentation structures trigger Liquid::StackLevelError. Flatten directory hierarchies immediately. Migrate to Eleventy’s computed data resolution for complex layouts.
Permalink Generation Conflicts
Default permalink: /{{ page.filePathStem }}/ creates duplicate slugs. This occurs when markdown files share names across subdirectories. Implement permalink: /{{ page.fileSlug }}/{{ page.date | date: '%Y/%m/%d' }}/. Use eleventyComputed.permalink for deterministic routing.
Kramdown TOC Corruption
Kramdown’s {:toc} parser breaks on custom HTML blocks. Unescaped Liquid tags also trigger failures. Add parse_block_html: true to _config.yml. Wrap custom extensions in {% raw %} tags to prevent AST corruption.
Frequently Asked Questions
Does Eleventy support incremental builds for 10k+ markdown files?
Yes. Requires the explicit --incremental flag and configured addWatchTarget paths. Disable eleventyComputed on non-essential data. This bypasses full cascade re-evaluation during local development.
Why does Jekyll fail to parse complex YAML frontmatter in technical docs?
Jekyll’s default SafeYAML parser restricts arbitrary Ruby objects. Resolve by setting safe_yaml: false in _config.yml. Migrate to TOML or JSON frontmatter for stricter schema validation.
Which SSG handles markdown table rendering better for API documentation?
Eleventy’s markdown-it supports GFM tables natively with zero configuration. Jekyll requires kramdown.input: GFM. Additional CSS overrides fix alignment and header styling inconsistencies.