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.

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.

Static Site Generators in Production