{
  "$type": "site.standard.document",
  "content": "---\ntitle: \"Switching from Jekyll to VitePress\"\ndescription: \"After ten years on Jekyll, I moved this site to VitePress. Notes on migrating\n  content, components, reveal.js slides, and data loading.\"\ntags: [dev, web]\n---\n\nThis site has run on Jekyll since 2014 (or even before, I think; but some\nsecrets are lost to time). Ten years is a good run for any technology choice,\nbut I've finally made the switch to [VitePress](https://vitepress.dev/). The\nsource is open, so you can\n[check it out for yourself](https://github.com/benswift/benswift.github.io/).\n\nJekyll still works fine for pure markdown→HTML, and so-called \"content sites\".\nThe problem is everything around the edges, and especially things which use\npackages from npm. My site has accumulated interactive components, reveal.js\nslide decks, custom syntax highlighting for obscure languages, and enough Ruby\nplugins that I'd forgotten what half of them did. And while Jekyll is mature, it\ndoes seem like it's kindof in maintenance mode these days.\n\nI'd been using VitePress for a few other projects and kept being impressed by\nhow much you get out of the box: Vue components and TypeScript, with a\nlightning-fast developer experience. Hot module replacement actually works,\nwhich is something Jekyll's `--livereload` never quite managed reliably.\n\nBut the real appeal is that the escape hatches are sensible. When you need\ncustom behaviour, you write TypeScript or Vue instead of Ruby plugins and Liquid\ntemplates. The mental model is cleaner: it's just Vite with some markdown\nconventions.[^vite]\n\n[^vite]:\n    If you're not familiar with [Vite](https://vite.dev/), it's the modern build\n    tool that replaced webpack in most of my projects. Fast, and also other\n    things, but mostly fast.\n\nIf you're gonna make the same move, here are a few things to note. Honestly,\njust point Claude at this blog post, because this sort of thing (moving from\none well-known stack to another) is the sort of thing that LLMs shine at. The\nmain shifts come down to four things: content structure, custom components,\nreveal.js slides, and data loading.\n\nJekyll wants posts in `_posts/YYYY-MM-DD-slug.md`. VitePress doesn't care, so I\nreorganised into `blog/YYYY/MM/DD/slug.md`. This preserves my existing URLs\nwhile making the directory structure more navigable.\n[Cool URIs don't change](https://www.w3.org/Provider/Style/URI).\n\nThe trick is extracting dates from the URL path rather than requiring them in\nfrontmatter. VitePress lets you do this with `transformPageData`:\n\n```ts\ntransformPageData(pageData) {\n  const dateMatch = pageData.relativePath.match(\n    /blog\\/(\\d{4})\\/(\\d{2})\\/(\\d{2})/\n  )\n  if (dateMatch) {\n    const [, year, month, day] = dateMatch\n    pageData.frontmatter.date = `${year}-${month}-${day}`\n  }\n}\n```\n\nFor custom components, all my Jekyll includes became Vue components. A YouTube embed that was 20 lines\nof Liquid with magic width calculations became:\n\n```vue\n<template>\n  <div class=\"youtube-wrapper\">\n    <iframe :src=\"embedUrl\" allowfullscreen />\n  </div>\n</template>\n```\n\nAt a certiain level of complexity, having a proper programming language (not\njust a templating system) becomes worth the tradeoffs. The component is\ntype-safe, composable, and I can actually read it six months later.\n\nReveal.js slides were the fiddly bit.\n\n:::info\n\nHonestly, the reveal.js stuff is still a work in progress, and I'm not super\nhappy with it just yet.\n\n:::\n\nI had a custom Jekyll plugin that transformed markdown into reveal.js slides.\nI rewrote this as a `markdown-it` plugin that runs at build time, splitting\ncontent on h1/h2 headers and hoisting `data-*` attributes onto the reveal\nsections.\n\nData loading rounds out the list. Jekyll has `_data` files that become\navailable in templates. VitePress has\n`.data.ts` files that can do the same thing---but with TypeScript, so you can\nparse BibTeX files, fetch from APIs, or do whatever processing you need:\n\n```ts\nexport default {\n  load() {\n    const publications = parseBibtex(\"_data/ben-pubs.bib\");\n    return publications.sort((a, b) => b.year - a.year);\n  },\n};\n```\n\nThe data is computed at build time and available in your pages. No runtime\noverhead.\n\nIn the wash-up, what I like about the new setup comes down to four things.\nThe dev experience is dramatically better, with instant hot reload and actual\nerror messages instead of Liquid stack traces. Type safety is end-to-end across\nconfiguration, data loaders and components, so I catch errors at build time\ninstead of discovering them in production. The component model is a better\nabstraction than Liquid includes; state, props, and slots map cleanly to what\nyou're actually trying to do. And the production build is smaller and faster,\nthough honestly this was never really a problem, since static sites are fast\nby definition.\n\nShould you switch? If your Jekyll site is just markdown and you're happy with\nit, stay put. Jekyll still does that well.\n\nBut if you're fighting Liquid templates, maintaining Ruby plugins you don't\nunderstand, or wanting to add interactive components without a whole separate\nbuild system, VitePress is worth considering. The migration isn't trivial, but\nit's straightforward if you take it piece by piece.\n\nThe old Jekyll site is preserved on the\n[`jekyll` tag](https://github.com/benswift/benswift.github.io/tree/jekyll) if\nyou want to compare. Or browse the current\n[`main` branch on GitHub](https://github.com/benswift/benswift.me) to see how it\nall fits together.\n\nIf there's something which was on the old site that you can't find on the new\none, then [drop me a line](mailto:ben@benswift.me) and I'll try to help you find\nit.\n",
  "createdAt": "2026-05-13T23:14:42.769Z",
  "description": "After ten years on Jekyll, I moved this site to VitePress. Notes on migrating content, components, reveal.js slides, and data loading.",
  "path": "/blog/2025/12/02/switching-from-jekyll-to-vitepress",
  "publishedAt": "2025-12-02T00:00:00.000Z",
  "site": "at://did:plc:tevykrhi4kibtsipzci76d76/site.standard.publication/self",
  "tags": [
    "dev",
    "web"
  ],
  "textContent": "After ten years on Jekyll, I moved this site to VitePress. Notes on migrating content, components, reveal.js slides, and data loading.",
  "title": "Switching from Jekyll to VitePress"
}