resume-toolkit allows you to produce beautiful HTML and PDF versions from your JSON Resume (and optionally, BibTeX) files.
Tip
Examples:
You can see a live real example at https://zzamboni.org/vita/.
You can find some further samples in the samples/ directory:
samples/example-resume/: fully synthetic example which shows a variety of features.samples/john-doe-brilliantcv/: the sample resume from Brilliant-CV (the one produced when you runtypst init @preview/brilliant-cv) converted to JSONresume format, to show the Typst rendering abilities (the resulting PDF is nearly identical).
Convert from JSON Resume and BibTeX files into:
- Resume HTML, using the jsonresume-theme-eventide theme;
- Resume Typst/PDF using the brilliant-cv theme;
- Standalone publications HTML page (from BibTeX);
- Standalone publications PDF (from BibTeX, rendered with Typst);
- Aggregated publications BibTeX.
Additional functionality:
- Updating the JSON Resume file with a list of certifications from Credly, including badges;
- Updating the JSON Resume file with a list of publications from BibTeX files (filtered by keywords and/or individual entries), and links to the standalone publications HTML page;
- Downloading company/school logos from logo.dev, to include in both HTML and PDF outputs.
Diagram source
The diagram above is automatically rendered from the source code below.flowchart TD
A[JSON Resume<br/>resume.json] --> C[resume-toolkit]
B["BibTeX files<br/>publications.bib ...<br/>(optional)"] .-> C
C -. update-* commands .-> A
subgraph CV[CV outputs]
D[HTML<br/>vita/index.html]
E[PDF<br/>vita/resume.pdf]
end
subgraph PUBS[Publications outputs]
F[HTML<br/>vita/publications/index.html]
G[PDF<br/>vita/publications/resume-pubs.pdf]
H[Aggregated BibTeX<br/>vita/publications/resume-pubs.bib]
end
C --> CV
C --> PUBS
classDef toolkit fill:#22d3ee,color:#0f172a,stroke:#0891b2,stroke-width:2px;
classDef io fill:#f8fafc,color:#0f172a,stroke:#94a3b8;
classDef hidden fill:none,stroke:none;
classDef ioBoxes fill:#ffffde
class C toolkit;
class A,B,D,E,F,G,H io;
class L,R dot;
class CV,PUBS ioBoxes;
Table of Contents
- Resume Toolkit - JSONresume/BibTeX to HTML/PDF
The recommended interface is the wrapper script build-resume.sh, which runs everything inside a Docker image.
- Linux or macOS (untested in Windows, should work if you can run bash scripts and have Docker installed)
- Docker or Podman (auto-detected, see also
VITA_CONTAINER_ENGINEbelow) - A file in JSON Resume format (with optional extensions as described below)
- Optional BibTeX file(s) for publications
To install, download the build-resume.sh script and make it executable:
wget https://raw.githubusercontent.com/zzamboni/resume-toolkit/refs/heads/main/build-resume.sh
chmod a+rx build-resume.shThe first time the script runs, it will download the Docker image automatically.
Build a Resume + publications:
build-resume.sh resume.json pubs-src/publications.bibBuild the bundled examples:
build-resume.sh samples/example-resume/example-resume.json --serveor
build-resume.sh samples/john-doe-brilliantcv/john-doe-brilliantcv.json --serveThen open http://localhost:8080 (the port may change if you run both at the same time, see the output for the correct URL).
Default output base directory:
build/<resume-stem>/
Generated files:
- CV HTML:
build/<resume-stem>/vita/index.html - CV Typst:
build/<resume-stem>/vita/<resume-stem>.typ - CV PDF:
build/<resume-stem>/vita/<resume-stem>.pdf - If BibTeX files are provided:
- Publications HTML:
build/<resume-stem>/vita/publications/index.html - Publications PDF:
build/<resume-stem>/vita/publications/<resume-stem>-pubs.pdf - Publications aggregated BibTeX:
build/<resume-stem>/vita/publications/<resume-stem>-pubs.bib
- Publications HTML:
$ build-resume.sh --help
Usage:
build-resume.sh [--pull] [build] <resume.json> [bibfiles...] [--out <dir>] [--pubs-url <url>] [--cv-url <url>] [--watch] [--serve] [--no-fetch-logos]
build-resume.sh [--pull] fetch-logos <resume.json> [--overwrite] [--dry-run] [--update-json] [--token LOGODEV_TOKEN]
build-resume.sh [--pull] update-logos <resume.json> [--overwrite] [--dry-run] [--token LOGODEV_TOKEN]
build-resume.sh [--pull] update-certs <username> <resume.json> [--include-expired] [--include-non-cert-badges] [--sort <date_desc|date_asc|name>]
build-resume.sh [--pull] update-pub-numbers <resume.json> [--html <path>]
build-resume.sh [--pull] versionbuild-resume.sh [--pull] [build] <resume.json> [bibfiles...] [--out <dir>] [--pubs-url <url>] [--cv-url <url>] [--watch] [--serve] [--no-fetch-logos]These are equivalent:
build-resume.sh build resume.json pubs-src/publications.bib
build-resume.sh resume.json pubs-src/publications.bibOptions:
--out <dir>: output base directory (defaultbuild/<resume-stem>)--pubs-url <url>: online publications URL for standalone publications PDF footer (can be specified in the JSON file withmeta.pdfthemeOptions.pubs_url)--cv-url <url>: online CV URL for main resume PDF footer (can be specified in the JSON file withmeta.pdfthemeOptions.cv_url)--pull: pull the configured Docker image before running and use the updated image if one is available--watch: rebuild on input changes--serve: start HTTP server (implies--watch)--no-fetch-logos: disable automatic logo fetching whenassets/logos/is missing
If no BibTeX files are provided on the command line, the pipeline can read them from a special entry in the publications section of your JSON resume:
"publications": [
{
"authors": ["Example Person"],
"bibfiles": ["pubs.bib", "patents.bib"]
}
]Only one publications[] entry may define bibfiles. If --bib arguments are provided, they take precedence. bibfiles entries are resolved relative to the JSON resume file location.
If no source assets/logos/ directory is found, the pipeline will automatically try to populate it by running the logo fetcher (see fetch-logos). If LOGODEV_TOKEN is not available, the build continues but emits a warning and skips automatic logo download. Use --no-fetch-logos to disable both the automatic fetch and the warning.
Download company/institution logos from the resume file into assets/logos/ in your working directory. Uses logo.dev to fetch logos. You need to create an API key and provide the publishable key in the LOGODEV_TOKEN environment variable, or using the --token flag.
If matching logo files are found under assets/logos/, the build step will include them automatically in the generated PDF. You can also provide/update the images by hand with the appropriate name (<company name>.png/jpg/jpeg/svg/webp/gif).
If called as update-logos or with the --update-json flag, it also updates the JSON resume file by writing the matching Logo.dev URLs into the image field of the corresponding work and education entries.
build-resume.sh fetch-logos resume.jsonOptions:
--overwrite: rewrite image files even if they already exist--dry-run: show what would be done--update-json: write image URLs back into the JSON file (in theimagefield ofwork/educationentries)--token <token>(or setLOGODEV_TOKEN): publishable key from Logo.dev
Sync certificates from Credly into your JSON resume. This replaces any entries in the certificates section of the JSONresume file that have a url field pointing to credly.com. Other entries are left untouched.
build-resume.sh update-certs <credly-username> resume.jsonOptions:
--include-expired--include-non-cert-badges--sort <date_desc|date_asc|name>(defaultdate_desc)
Update publication reference numbers in your JSON resume using the generated publications HTML anchors.
build-resume.sh update-pub-numbers resume.jsonOptions:
--html <path>(defaults tobuild/<resume-stem>/vita/publications/index.html)
Replace inline publications[] entries in your JSON resume from the BibTeX selection defined by the generated-publications entry.
build-resume.sh update-inline-pubs resume.jsonOptional BibTeX files can be passed explicitly to override the bibfiles configured in publications[]. The command keeps the single special publications[] entry with bibfiles and regenerates all other publications[] entries using the JSON Resume schema fields name, publisher, releaseDate, url, and summary.
build-resume.sh shellGives you an interactive shell inside the container.
The toolkit can read BibTeX sources from a special publications[] entry in your JSON Resume. This is the preferred way to define generated publications output. For example:
"publications": [
{
"authors": ["Example Person"],
"bibfiles": ["pubs.bib", "patents.bib"]
}
]Rules for this entry:
- Only one
publications[]entry may definebibfiles. bibfilesare resolved relative to the JSON resume file.- If
nameis omitted, it defaults to"Full list online". - If
urlis omitted, it defaults to"publications/". - If
--bibarguments are provided on the command line, they take precedence overbibfilesfrom the JSON.
That bibfiles entry is used to generate:
- the standalone publications HTML page
- the standalone publications PDF
- the downloadable aggregated BibTeX file
The regular JSON Resume publications[] entries are still rendered normally in the HTML CV. If you want those inline HTML publications to be generated from BibTeX, use update-inline-pubs.
The generated-publications entry may also define:
bibentries: explicit BibTeX entry keysbibkeywords: BibTeXkeywordsvalues to match
Selection is additive: an entry is included if it matches either bibentries or bibkeywords.
"publications": [
{
"authors": ["Example Person"],
"bibfiles": ["pubs.bib", "patents.bib"],
"bibentries": ["example2024paper"],
"bibkeywords": ["selected", "important"]
}
]If meta.publicationsOptions.inline_in_pdf is enabled, the resume PDF embeds publications directly using Typst and pergamon. In this case any individually-specified entries in the JSON file are ignored.
- If
inline_in_pdfistrue, these defaults are used:ref-style: "ieee"ref-full: trueref-sorting: "ydnt"
- If
inline_in_pdfis an object, you can override those values:ref-styleref-fullref-sorting
Example:
"meta": {
"publicationsOptions": {
"inline_in_pdf": {
"ref-style": "ieee",
"ref-full": false,
"ref-sorting": "ydnt"
}
}
}The inline PDF bibliography always uses the filtered selection from bibentries / bibkeywords, when those are present.
meta.publicationsOptions.full_standalone_list controls whether the standalone publications outputs use the full list from bibfiles, or the same filtered subset used inline in the PDF CV.
true(default): standalone publications HTML/PDF/BibTeX use the full list, while the inline PDF bibliography can still be filteredfalse: standalone publications HTML/PDF/BibTeX use the same filtered subset as the inline PDF bibliography
You can also set:
full_standalone_list_title: title for the standalone publications HTML/PDF pages- defaults to
"Publications"
- defaults to
You can configure publication sectioning for both the standalone publications page and the PDF bibliography outputs via meta.publicationsOptions:
pubSections: falseor unset: no sectioning (single list)pubSections: true: use the default section order and titlespubSections: ["..."]: custom section order and selection
Section names are matched against BibTeX keywords. Optional title overrides go in:
pubSectionTitles
If pubSections is true, these defaults are used:
DEFAULT_SECTION_ORDER = [
"book",
"editorial",
"thesis",
"refereed",
"techreport",
"presentations",
"invited",
"patent",
"other",
]
DEFAULT_SECTION_TITLES = {
"book": "Books",
"editorial": "Editorial Activities",
"thesis": "Theses",
"refereed": "Refereed Papers",
"techreport": "Technical Reports",
"presentations": "Presentations",
"invited": "Invited Talks and Articles",
"patent": "Patents",
"other": "Other Publications",
}meta.publicationsOptions.links controls the floating links shown on the standalone publications HTML page.
If links is unset, these defaults are generated:
[
{
"name": "PDF",
"url": "<publications>.pdf",
"icon": "file-pdf"
},
{
"name": "BibTeX",
"url": "<publications>.bib",
"icon": "tex"
}
]Notes:
<publications>is replaced with the generated publications file stem for the current resume<resume>is replaced with the main resume file stem- If
linksis present but empty ([]), no floating links are rendered - Icons can be plain Font Awesome names such as
file-pdf, or class-style strings such asfa-regular fa-file-pdforfa-brands fa-github
"publications": [
{
"authors": ["Example Person"],
"bibfiles": ["pubs.bib", "patents.bib"],
"bibkeywords": ["selected", "important"],
"bibentries": ["example2024paper"]
}
],
"meta": {
"publicationsOptions": {
"inline_in_pdf": {
"ref-style": "ieee",
"ref-full": false,
"ref-sorting": "ydnt"
},
"full_standalone_list": true,
"full_standalone_list_title": "Research Output",
"links": [
{
"name": "PDF",
"url": "<publications>.pdf",
"icon": "file-pdf"
},
{
"name": "BibTeX",
"url": "<publications>.bib",
"icon": "tex"
}
],
"pubSections": ["refereed", "patent", "other"],
"pubSectionTitles": {
"refereed": "Journal Articles",
"patent": "Patents",
"other": "Other Publications"
}
}
}The HTML theme used by this toolkit is jsonresume-theme-eventide, which is the source of truth for HTML theme configuration and behavior.
In resume-toolkit, all meta.themeOptions values are passed through to Eventide for HTML rendering. The toolkit also adds a few defaults before rendering:
- if
meta.themeOptions.linksis not set, default floating links are generated for the resume PDF and, when applicable, the standalone publications page - if
meta.themeOptions.footer_rightis not set, it defaults toPowered by [resume-toolkit](https://github.com/zzamboni/resume-toolkit) <resume>and<publications>placeholders inmeta.themeOptions.links[*].urlare expanded before rendering
A small subset of meta.themeOptions also affects PDF rendering:
sections: controls section order and selection in both HTML and PDFsectionLabels: controls section labels in both HTML and PDFprojectsByType: controls project grouping in both HTML and PDF
For inline PDF publications, sectionLabels.publications is also used as the fallback publications section label unless overridden by meta.publicationsOptions.full_standalone_list_title.
PDF output is rendered with Typst using the brilliant-cv package. Toolkit-specific PDF options live under meta.pdfthemeOptions.
meta.pdfthemeOptions.layout is deep-merged into the default metadata.layout used for the generated Typst document.
Current defaults are:
DEFAULT_PDF_THEME_LAYOUT = {
"awesome_color": "skyblue",
"before_section_skip": "1pt",
"before_entry_skip": "1pt",
"before_entry_description_skip": "1pt",
"paper_size": "a4",
"fonts": {
"regular_fonts": ["Source Sans 3"],
"header_font": "Roboto",
},
"header": {
"header_align": "left",
"display_profile_photo": True,
"profile_photo_radius": "50%",
"info_font_size": "10pt",
},
"entry": {
"display_entry_society_first": True,
"display_logo": True,
},
"footer": {
"display_page_counter": False,
"display_footer": True,
},
}Any fields you do not set keep these defaults.
Example:
{
"meta": {
"pdfthemeOptions": {
"layout": {
"awesome_color": "red",
"header": {
"header_align": "center",
"info_font_size": "9pt"
},
"footer": {
"display_page_counter": true
}
}
}
}
}Three keys under meta.pdfthemeOptions.layout are handled specially by the toolkit rather than being passed directly into metadata.layout:
highlightedletterssummary_title
highlighted and letters control how section titles are rendered in the generated Typst:
- if they are set, they are passed explicitly to
#cv-section(...) - if they are omitted, nothing is passed, so
brilliant-cvuses its own defaults
summary_title controls whether a non-empty summary is preceded by a Summary heading in the PDF. It defaults to false.
Example:
{
"meta": {
"pdfthemeOptions": {
"layout": {
"highlighted": false,
"letters": 3,
"summary_title": true
}
}
}
}At the meta.pdfthemeOptions level, the toolkit also supports:
visible_urlscv_urlpubs_url
visible_urls controls where compact printable URLs are shown in the PDF output. It defaults to:
["notes"]Supported values are:
notesprofilesprojectsallnone
cv_url is shown in the footer of the main CV PDF.
pubs_url is shown in the footer of the standalone publications PDF.
Command-line --cv-url and --pubs-url values override these config entries.
Example:
{
"meta": {
"pdfthemeOptions": {
"visible_urls": ["notes", "profiles"],
"cv_url": "https://example.com/vita/",
"pubs_url": "https://example.com/vita/publications/"
}
}
}If you set meta.site.url, relative links remain relative in HTML output but are resolved against that base URL in generated PDF output. This applies to:
- explicit
urlfields - Markdown links embedded in text fields such as
summaryorhighlights
The standalone publications PDF uses the same meta.pdfthemeOptions.layout settings and section-title styling, but it also forces a few document-specific overrides:
- the profile photo is always disabled
- the footer label uses the standalone publications title
meta.pdfthemeOptions.pubs_urlis rendered in that footer when set
Some behavior can be configured using environment variables. The only mandatory one (if you want to fetch logos) is LOGODEV_TOKEN.
LOGODEV_TOKEN: token used byfetch-logosVITA_PIPELINE_IMAGE: Docker image (default:ghcr.io/zzamboni/resume-toolkit:latest)VITA_CONTAINER_ENGINE: container engine to use (dockerorpodman). If unset,dockeris used when available, otherwisepodman.VITA_SERVE_PORT: serve port (default:8080)VITA_PIPELINE_CACHE_DIR: host cache dir for container caches
The wrapper runs:
- containerized entrypoint in
docker/entrypoint.sh - pipeline script
scripts/run_pipeline.sh - supporting converters in
scripts/
The container uses themes/jsonresume-theme-eventide as a git submodule, pointing to https://github.com/zzamboni/jsonresume-theme-eventide.
Clone with submodules enabled:
git clone --recurse-submodules https://github.com/zzamboni/resume-toolkit.gitIf you already cloned without submodules:
git submodule update --init --recursiveYou can build the Docker image locally with:
mise toolkit-image-buildContainer integration tests live under tests/container/.
Run tests:
mise test-toolkit