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.
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.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.
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:
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.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.index.Rmd) are copied to
assets/img/posts/<post>/ and relative image links are
rewritten.[[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.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.params.author), url (from
baseURL) and description are written into
_config.yml, including jekyll-scholar’s author-highlighting
names.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.{{< 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.{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.
Knit everything (no Jekyll needed) and read the output:
Things worth checking in _posts/*.md:
published: false; delete or
finish them.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.migrate_hugo() fills in your identity (name,
url, description) but al-folio’s rich
_config.yml deserves a pass by hand:
baseurl (usually "" for a user page)
and browse the config for features you want (Google Analytics, comments,
…)._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 = "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.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().Verified with al-folio as of September 2026:
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.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.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.
youruser.github.io and push
the new site to it (including _source/ – it is excluded
from the Jekyll build but belongs in version control)..github/workflows/deploy.yml) builds and publishes the
site on every push.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.