Migrating from blogdown/Hugo to jekylldown + al-folio

al-folio is the de-facto reference theme for academic sites – publications generated from BibTeX, news, CV, and teaching pages as structured data. Hugo has no equivalent, which is the main reason to move a blogdown site to Jekyll. This vignette walks through that migration with jekylldown, keeping your posts in R Markdown throughout.

al-folio is the vignette’s focus, but migrate_hugo() targets any of the supported themes – theme = "chirpy", "minimal-mistakes" or "minima" work the same way, with pages, navigation, avatar and social profiles placed in each theme’s own convention (Chirpy _tabs/, Minimal Mistakes _pages/ + _data/navigation.yml + the author: sidebar, …). Steps 1, 2 and 4 apply to all of them; step 3 is al-folio-specific.

Before you start

  1. Work on a copy. migrate_hugo() never writes into the source site, but be conservative anyway: leave your production repository untouched until the new site builds and looks right.
  2. Diagnose your toolchain:
jekylldown::check()

A local Ruby/Jekyll is optional for this migration: knitting posts only needs R, and al-folio ships GitHub Actions workflows that build the site remotely. You only need Jekyll (plus bundle_install(), below) for local previews.

Step 1 – convert the site

library(jekylldown)

migrate_hugo(
  from  = "path/to/my-hugo-site",     # blogdown/Hugo site (read-only)
  to    = "path/to/my-new-site",      # created; must not exist yet
  theme = "al-folio",                 # fetched from GitHub (network needed)
  only_referenced = TRUE,             # skip static files nothing links to
  publications = "bib",               # the default; papers.bib from the DOIs (see below)
  theme_color = "red"                 # al-folio accent color (see below)
)

The Hugo config (config.toml/.yaml) is read first and drives the migration. What gets converted automatically:

  • Posts under content/post/ (also posts/, blog/) are renamed to Jekyll’s YYYY-MM-DD-slug convention. .Rmd/.Rmarkdown sources go to _source/ and will be re-knitted by jekylldown; plain .md posts go straight to _posts/. Filename slugs are kept, so post URLs are preserved.
  • Front matter (YAML or TOML) becomes Jekyll YAML: layout: post is added, draft: true becomes published: false, aliases becomes redirect_from (enable the jekyll-redirect-from plugin if you use it), and Hugo-only keys are dropped.
  • Page bundle resources (images next to index.Rmd) are copied to assets/img/posts/<post>/ and relative image links are rewritten.
  • Menu pages. Every page referenced by the Hugo menu ([[menu.main]]) – content/publications.md, talks.md, … – becomes an al-folio page in _pages/ with the same URL (permalink) and the menu position (nav_order). A menu entry pointing at the posts section repositions the theme’s blog page instead.
  • The home page. content/_index.md becomes the body of al-folio’s about.md landing page; a {{< figure class="avatar" >}} shortcode turns into the theme’s profile picture.
  • Site identity. Your name (from the home page title or params.author), url (from baseURL) and description are written into _config.yml, including jekyll-scholar’s author-highlighting names.
  • Theme demo content is scrubbed: al-folio’s sample posts, news, projects, books, teachings and the Einstein pages are removed, so the site starts empty instead of generic.
  • static/ is copied to the site root, so absolute URLs like /images/foo.png keep working. With only_referenced = TRUE only files actually referenced by the migrated content (plus favicons) are copied – old Hugo static/ folders accumulate junk.
  • Shortcodes: {{< figure >}} and {{< youtube >}} are converted; everything else is left in place and listed in the migration report. A figure’s class/width/height become a kramdown attribute list, and the CSS rules behind those classes are ported from the Hugo theme’s sass (variables resolved) into the new site’s stylesheet, so images keep their original display size instead of rendering at natural size.
  • Pandoc image attributes ({width=100%}) are rewritten as kramdown inline attribute lists ({: width="100%"}).

The report printed at the end lists what is left: posts with remaining shortcodes and content not referenced by the Hugo menu (dead pages, custom layouts/) – review that list before assuming it can be dropped.

Step 2 – review the posts

Knit everything (no Jekyll needed) and read the output:

build_site("path/to/my-new-site", local_jekyll = FALSE)

Things worth checking in _posts/*.md:

  • Posts using HTML widgets (plotly, leaflet, …) render as static Markdown here; widgets that relied on blogdown’s HTML pipeline need rework (e.g. save a static image, or embed the widget’s HTML manually).
  • Shortcodes flagged in the report need a manual equivalent (a Liquid include, plain HTML, or just Markdown).
  • Draft posts were kept with published: false; delete or finish them.
  • Posts needing pandoc-only features – citations (bibliography:), footnotes, cross-references – can opt in with knit_method: pandoc in the front matter (needs rmarkdown + pandoc). New posts can also be written in Quarto (new_post(format = "qmd")), which brings those features natively via the Quarto CLI.

Step 3 – adapt the al-folio configuration

migrate_hugo() fills in your identity (name, url, description) but al-folio’s rich _config.yml deserves a pass by hand:

  • Check baseurl (usually "" for a user page) and browse the config for features you want (Google Analytics, comments, …).
  • Social icons live in _data/socials.yml. migrate_hugo() fills it from the profile links found in your Hugo pages (GitHub, LinkedIn, ORCID, Google Scholar, ResearchGate, StackOverflow, …), plus your e-mail address and a CV/resume PDF link – check the migration report for what was recognized and add anything missing by hand.
  • Publications: with publications = "bib" (the default) the DOIs on the page are extracted and fetched as BibTeX from doi.org, so al-folio’s jekyll-scholar renders the publications page with the theme’s badges and buttons – deterministic and reproducible (the DOI registry, not a heuristic parser, is the source of truth). Publications without a DOI are listed in the report; add them to _bibliography/papers.bib by hand. publications = "html" migrates your hand-written page as-is instead – that is also the automatic fallback when the page has no DOI or there is no network, but note that a page built around raw HTML (floats, flex <div>s) rarely survives kramdown intact.
  • Accent color: al-folio defaults to purple. theme_color = "red" (or any palette name / hex value) sets it at migration time, and set_theme_color() changes it later – it needs the theme gems, so on a first machine run bundle_install() and then set_theme_color().

Known al-folio gotchas

Verified with al-folio as of September 2026:

  • Jupyter notebooks. al-folio refuses to build when the jekyll-jupyter-notebook plugin is enabled but jupyter-nbconvert is not on the PATH. Since a freshly migrated site has no .ipynb posts, migrate_hugo() removes the plugin from the Gemfile and from _config.yml for you. If you later want to write posts as notebooks, add both entries back and install Jupyter.
  • JavaScript minification. Terser wants a JS runtime (Node.js). The build succeeds without one – assets are just not minified – so this only matters for production polish. GitHub Actions provides Node, so remote builds are unaffected.
  • Dependabot alert on css_parser. Once the site is on GitHub, Dependabot flags css_parser 1.22.0 in Gemfile.lock (CVE-2026-53727). The alert comes from the theme, not from jekylldown: al-folio’s jekyll-3rd-party-libraries plugin requires a version below 2.0, so bundler cannot move to the fixed 3.0.0 until the plugin allows it. The gem runs only during the build, on your own CSS, so the practical risk is low. You can dismiss the alert; a later bundle_install() picks up the fix once al-folio ships it.

Step 4 – preview locally (optional)

al-folio manages its plugins with Bundler, so install its gems once into jekylldown’s isolated environment with bundle_install("path/to/my-new-site"), then serve:

serve_site("path/to/my-new-site")
# ... edit _source/*.Rmd; saving re-knits and reloads the browser
stop_server("path/to/my-new-site")

After bundle_install(), build_site() and serve_site() automatically use bundle exec jekyll, so the theme’s plugins are found.

Step 5 – deploy on GitHub Pages

  1. Create a repository named youruser.github.io and push the new site to it (including _source/ – it is excluded from the Jekyll build but belongs in version control).
  2. In the repository settings, enable GitHub Pages with GitHub Actions as the source: al-folio’s bundled workflow (.github/workflows/deploy.yml) builds and publishes the site on every push.
  3. Your R workflow from then on: new_post("Title", dir = site), write, build_site(site) (or just knit via build_site(site, local_jekyll = FALSE)), commit, push. No more two-repository setup as with blogdown – the source and the built site live in one repo, built remotely.

Migration checklist