curriculr is an R package for producing data-driven curriculum vitae documents. You maintain your CV content in an Excel workbook. curriculr reads it, converts it into Typst layout blocks, and renders a polished PDF via Quarto’s Typst engine. No LaTeX. No vitae. No custom .cls files.
Installation
You can install curriculr from CRAN:
install.packages("curriculr")Install the development version from GitHub:
# install.packages("pak")
pak::pak("erwinlares/curriculr")For new users
Step 1 — scaffold your project
Call create_cv() with no arguments. This copies the template workbook and a placeholder profile image into your current working directory:
You will see:
v Created cv-data-template.xlsx
v Created placeholder.png
i Next steps:
1. Open cv-data-template.xlsx and fill in the profile sheet.
2. Replace placeholder.png with your own profile photo.
3. Call create_cv(data = 'cv-data-template.xlsx', photo = 'your-photo.png')
Step 2 — fill in the workbook
Open cv-data-template.xlsx. Start with the profile sheet:
| field | value |
|---|---|
| first_name | Your first name |
| last_name | Your last name |
| job_title | Your current title |
| address | Your mailing address |
| your@email.edu | |
| website | yourwebsite.com |
| github | your-github-username |
| in/your-linkedin | |
| profile_statement | One or two sentence professional summary |
| photo | Relative path to your profile image |
Then fill in the remaining sheets with your CV content. The readme sheet inside the workbook explains the column schema and date entry conventions in detail.
Step 3 — render
create_cv(
data = "cv-data-template.xlsx",
photo = "your-photo.png"
)This writes CV.qmd and CV.pdf next to your workbook. Open CV.pdf to review the output. To update your CV, edit the workbook and call create_cv() again with overwrite = TRUE.
To render without a profile photo, omit the photo argument. The header will use a single-column layout with your name, contact line, and profile statement centered on the page:
create_cv(data = "cv-data-template.xlsx")For existing users updating from v0.2.0
v0.3.0 introduces several workbook schema changes and new arguments. Here is what you need to know.
photo = NULL now means no photo. Previously, omitting photo in render mode fell back to a bundled placeholder image. It now produces a single-column header layout with no image. Supply a path explicitly to restore the two-column layout.
Your workbook needs an include_in_resume column on every section sheet. This is a boolean column (checked/unchecked) that controls which rows appear when rendering in resume variant mode. Add a column named include_in_resume to each section sheet and check the rows you want to include in a focused resume. Rows without this column are always included.
Your workbook can have an optional theme sheet. This sheet controls fonts, colors, and page layout. If absent, built-in defaults are used. Copy the theme sheet from the updated template workbook to gain full control over the visual appearance without editing CV.qmd directly.
readxl is no longer required. curriculr now uses openxlsx2 for all workbook reading. If you have readxl in your own explicit dependencies for curriculr-related code, you can remove it.
For existing users updating from v0.1.0
v0.2.0 changes how create_cv() works and adds a sections sheet to the workbook schema. Here is what you need to know.
create_cv() now has two modes:
- Scaffold mode (no arguments) — copies template files to your current directory. Use this once to get started or to reset.
- Render mode (with arguments) — reads your workbook and renders the PDF. Use this every time you update your CV.
Your workbook needs a sections sheet. This sheet controls which sections appear in the rendered PDF and in what order. Add it manually or copy it from the updated template workbook. The schema is:
| section | label | title_col | org_col | detail_col | date_fun | where_col |
|---|---|---|---|---|---|---|
| education | Education | title | institution | detail | year | where |
| experience | Experience | title | unit | detail | date | where |
Row order is render order. Delete a row to exclude a section. Add a row for any sheet that follows the standard column schema and it will render automatically without any R code changes.
The date_fun column accepts five tokens:
| token | produces |
|---|---|
date |
Jan 2018 - Dec 2022 |
year |
2018 - 2022 |
month_year |
Jan 2018 |
year_only |
2021 |
none |
(no date) |
Rendering a resume
When you want a shorter, focused version of your CV, use variant = "resume". curriculr will include only the rows you have checked in the include_in_resume column of each section sheet:
create_cv(
data = "cv-data-template.xlsx",
photo = "your-photo.png",
variant = "resume",
output_file = "resume.pdf"
)Check and uncheck rows directly in the workbook. No R code changes are needed to switch between CV and resume variants.
Customizing the rendered PDF
Changing colors, fonts, and page layout
Open your workbook and edit the theme sheet. Each key maps to a visual setting:
| key | what it controls |
|---|---|
font_family |
Body font (e.g. Lato, Arial) |
font_size |
Base font size (e.g. 8.8pt) |
body_color |
Main text color (hex code) |
accent_color |
Section headings, dates, and rules (hex code) |
dark_color |
Entry titles and name in the header (hex code) |
papersize |
Page size (us-letter or a4) |
margin_x |
Left and right margins (e.g. 0.62in) |
margin_y |
Top and bottom margins (e.g. 0.58in) |
Re-render after editing the theme sheet with overwrite = TRUE to see the changes.
Changing section order or content
Edit the sections sheet in your workbook. Reorder rows to reorder sections. Delete a row to remove a section.
Adding a new section
Use add_section() to add a new sheet and register it in the sections control sheet in one step:
add_section(
"cv-data-template.xlsx",
section = "patents",
label = "Patents",
date_fun = "year_only"
)The new sheet is pre-populated with the standard column spine. Open the workbook, add your data, and re-render.
Functions
create_cv()
The main entry point. No arguments triggers scaffold mode. With data triggers render mode.
# Scaffold mode
create_cv()
# Render mode — full CV with Font Awesome icons in the contact line
create_cv(
data = "~/my_cv/cv-data.xlsx",
photo = "~/my_cv/me.jpeg",
output_file = "erwin-lares-cv.pdf"
)
# Render mode — resume variant, plain text contact line
create_cv(
data = "~/my_cv/cv-data.xlsx",
photo = "~/my_cv/me.jpeg",
variant = "resume",
use_icons = "none",
output_file = "erwin-lares-resume.pdf"
)Arguments:
-
data— path to the workbook.NULLtriggers scaffold mode. -
photo— path to the profile image.NULLrenders without a photo. -
output_file— PDF filename. Defaults to"CV.pdf". -
overwrite— whether to overwrite existing files. Defaults toFALSE. -
variant—"cv"(all rows) or"resume"(checked rows only). -
use_icons—"fontawesome"(default) or"none".
add_section()
Adds a new sheet to an existing workbook and registers it in the sections control sheet.
add_section(
"cv-data-template.xlsx",
section = "invited_talks",
label = "Invited Talks",
date_fun = "month_year",
where_col = "where"
)
read_cv_data()
Reads all sheets from the workbook into a named list. The profile and theme sheets become named character vectors; section sheets become data frames; the sections sheet is returned in row order.
cv <- read_cv_data("cv-data-template.xlsx")
cv <- read_cv_data("cv-data-template.xlsx", variant = "resume")
cv$experience # a data frame
cv$sections # the sections control sheet
cv$profile[["email"]] # a scalar string
cv$theme[["accent_color"]] # a scalar string
cv_contact_line()
Assembles the contact line from a profile vector. Useful when building custom Quarto templates.
cv_contact_line(cv$profile, use_icons = "fontawesome")
cv_contact_line(cv$profile, use_icons = "none")
cv_render_section()
Iterates over a CV data frame and writes each row as a formatted Typst entry. Called inside CV.qmd — you will not normally call this directly.
typst_escape()
Escapes Typst-sensitive characters in a string. Useful when building custom Quarto templates.
Workbook schema
Every section sheet shares a common column spine:
title | unit | startMonth | startYear | endMonth | endYear | where | detail | include_in_resume
title is always the primary entry label. unit is the secondary line. include_in_resume is a boolean checkbox column — check a row to include it when rendering with variant = "resume". Leave cells blank rather than typing NA.
Related packages
curriculr is one of three sibling packages:
- toolero — research workflow toolkit
- containr — Docker containerization
- curriculr — data-driven CV generation with Quarto and Typst
Acknowledgements
curriculr was inspired by two prior projects:
- vitae by Mitchell O’Hara-Wild, Rob Hyndman, and contributors
- Awesome CV by Byungjin Park (posquit0)
