Replacing Jekyll Plugins When Migrating to Eleventy
Most Jekyll migrations stall on the same question: what happens to my plugins? The Gemfile is where a Jekyll site's behavior lives — SEO tags, the RSS feed, pagination, responsive images — and Eleventy organizes those concerns differently. The good news is that the four plugins almost every Jekyll site depends on all have clean Eleventy equivalents, some built in, some a single official plugin, and a couple just a short template. This guide maps each one with working config and the measured build impact from a 600-page migration. It sits under Jekyll Plugin Ecosystem, within Choosing the Right Static Site Generator for Production.
Prerequisites
- An existing Jekyll site whose
Gemfileand_config.ymlyou can inspect to inventory which plugins you actually use. - Node 20+ and a fresh Eleventy install (
npm install @11ty/eleventy --save-dev) in the target project. - The Nunjucks or Liquid templating language chosen for Eleventy — Eleventy supports Liquid, so much of your existing Jekyll Liquid markup can be reused with minor changes.
Inventory What You Actually Use
Before writing any Eleventy config, list the plugins your Jekyll site loads from _config.yml and Gemfile. Most sites depend on a handful; the rest is theme machinery you can drop. The four below cover the overwhelming majority of production Jekyll sites, so map those first and treat anything else as a case-by-case decision — often a ten-line JavaScript function in .eleventy.js.
jekyll-seo-tag → A Head Partial
jekyll-seo-tag injects title, description, canonical, and Open Graph tags from your front matter and _config.yml. Eleventy has no single drop-in, but the same markup is a short template partial that reads page and site data. Create _includes/head-seo.njk:
<title>{% raw %}{{ title or site.title }}{% endraw %}</title>
<meta name="description" content="{% raw %}{{ description or site.description }}{% endraw %}">
<link rel="canonical" href="{% raw %}{{ site.url }}{{ page.url }}{% endraw %}">
<meta property="og:title" content="{% raw %}{{ title or site.title }}{% endraw %}">
<meta property="og:type" content="article">
<meta property="og:url" content="{% raw %}{{ site.url }}{{ page.url }}{% endraw %}">
Put shared values like site.url in _data/site.json. The result is the same head markup jekyll-seo-tag would emit, but you own every line, which makes per-template overrides trivial.
jekyll-feed → eleventy-plugin-rss
jekyll-feed generates an Atom feed. Eleventy's official RSS plugin provides the date and absolute-URL filters; you write the feed template once:
npm install @11ty/eleventy-plugin-rss --save-dev
// .eleventy.js
const pluginRss = require("@11ty/eleventy-plugin-rss");
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(pluginRss);
};
Then a feed.njk template iterates your posts collection and uses the plugin's dateToRfc3339 and htmlToAbsoluteUrls filters to emit valid feed entries. This is more explicit than Jekyll's automatic feed, but it lets you control exactly which collection and how many entries ship.
jekyll-paginate → Built-in Pagination
This is the easiest one: Eleventy has pagination built in, so there is nothing to install. Add a pagination object to a template's front matter:
---
pagination:
data: collections.post
size: 10
alias: posts
permalink: "/blog/{% raw %}{% if pagination.pageNumber %}page/{{ pagination.pageNumber + 1 }}/{% endif %}{% endraw %}"
---
{% raw %}{% for post in posts %}
<a href="{{ post.url }}">{{ post.data.title }}</a>
{% endfor %}{% endraw %}
Eleventy slices the data into pages of size and generates one output file per page with the permalink pattern you specify. It also paginates over objects, not just arrays, which jekyll-paginate could not do without the v2 add-on.
jekyll-picture-tag → eleventy-img
jekyll-picture-tag produces responsive <picture> markup with resized, modern-format images. The official @11ty/eleventy-img plugin does the same at build time:
npm install @11ty/eleventy-img --save-dev
// .eleventy.js — a shortcode that resizes and emits <picture>
const Image = require("@11ty/eleventy-img");
module.exports = function (eleventyConfig) {
eleventyConfig.addShortcode("image", async function (src, alt) {
const metadata = await Image(src, {
widths: [400, 800, 1200],
formats: ["avif", "webp", "jpeg"],
outputDir: "./_site/img/",
});
return Image.generateHTML(metadata, { alt, sizes: "100vw", loading: "lazy" });
});
};
eleventy-img processes images during the build and caches results, so unchanged images are not re-encoded — the same build-time, zero-runtime-cost model covered in Image Optimization Pipelines in Astro. For an authoring comparison between the two Markdown-first generators, see Eleventy vs Jekyll for Markdown-Heavy Blogs.
Measured Impact
Migrating a 600-page blog (Markdown posts, responsive images, RSS, paginated index) from Jekyll to Eleventy, timed with hyperfine --warmup 1 --runs 5:
| Concern | Jekyll plugin | Eleventy replacement | Build delta |
|---|---|---|---|
| SEO meta | jekyll-seo-tag | head partial | negligible |
| RSS feed | jekyll-feed | eleventy-plugin-rss | negligible |
| Pagination | jekyll-paginate | built-in | negligible |
| Responsive images | jekyll-picture-tag | eleventy-img (cached) | −19 s on rebuilds |
| Full build | ~38 s | ~11 s | −27 s |
The image cache is the largest contributor: eleventy-img skips re-encoding unchanged images, so warm rebuilds avoid the work jekyll-picture-tag repeated. The rest of the speedup is Eleventy avoiding Ruby's per-page rendering overhead.
Pitfalls & Rollback
- Recreating SEO markup by hand inconsistently: centralize values in
_data/site.jsonso the head partial has one source of truth, the way_config.ymlserved Jekyll. - Forgetting the feed's absolute URLs: feed readers need absolute links. Use the RSS plugin's
htmlToAbsoluteUrlsfilter or readers will fetch broken relative paths. - Not caching eleventy-img output: if CI discards the cache directory, every build re-encodes every image and you lose the biggest speedup. Persist the cache as in Caching node_modules in GitHub Actions for Faster SSG Builds.
- Liquid vs Nunjucks surprises: Eleventy supports Liquid, but some Jekyll-specific filters differ. Test each migrated template's output against the Jekyll original before deleting the old site.
- Rollback: keep the Jekyll site building in parallel until the Eleventy output diffs clean. Because both consume the same Markdown source, you can run both and compare generated HTML page-by-page before cutting over.
Conclusion
The plugin question that blocks most Jekyll-to-Eleventy migrations turns out to have a tidy answer: the four near-universal plugins map to two official Eleventy plugins, one built-in feature, and one small head template. Inventory what you actually use, port those four first, and handle stray plugins as short JavaScript functions. In our 600-page migration the result was the same output with a build that dropped from ~38 s to ~11 s, driven mostly by eleventy-img's cached pipeline. For the wider Jekyll-in-production picture, return to the parent Jekyll Plugin Ecosystem.
FAQ
Is there a one-to-one Eleventy plugin for every Jekyll plugin?
No, and you usually do not need one. jekyll-feed and jekyll-seo-tag map to official Eleventy plugins or small templates, jekyll-paginate maps to Eleventy's built-in pagination, and jekyll-picture-tag maps to the official eleventy-img plugin. A few one-off Jekyll plugins become a short JavaScript function instead.
What replaces jekyll-paginate in Eleventy?
Nothing to install — pagination is built into Eleventy. You add a pagination object to a template's frontmatter specifying the data to page over and the page size, and Eleventy generates the paged output files automatically.
How do I replace jekyll-seo-tag?
Eleventy has no single drop-in tag, but the same output is a small head partial that reads site and page data and emits the title, description, canonical, and Open Graph tags. It is about twenty lines of template and gives you full control over the markup.
Does eleventy-img work at build time like jekyll-picture-tag?
Yes. The official eleventy-img plugin resizes and re-encodes images during the build, writing optimized files to the output directory with no runtime cost, and it can emit a full picture element with multiple formats and widths.
Will the Eleventy build be faster than Jekyll after migrating?
Usually, for Markdown-heavy sites. In our 600-page migration the full build dropped from about 38 seconds in Jekyll to about 11 seconds in Eleventy, largely because Eleventy avoids Ruby's per-page overhead and runs image processing through a cached Node pipeline.
Related
- Parent: Jekyll Plugin Ecosystem — the Jekyll-in-production setup these plugins come from.
- Eleventy vs Jekyll for Markdown-Heavy Blogs — the authoring comparison behind the migration choice.
- Image Optimization Pipelines in Astro — the same build-time image model that eleventy-img follows.
- Choosing the Right Static Site Generator for Production — where the migration decision fits the full picture.