Provides tools for producing data-driven curriculum vitae documents from structured data stored in an Excel workbook. The core workflow reads CV content from a workbook, converts it into 'Typst' layout blocks, and renders a polished PDF via the 'Quarto' publishing system. Includes functions for reading and cleaning CV data, building 'Typst' section headings and entries, rendering CV sections from data frames, and scaffolding new CV projects with a standard folder structure and template workbook. Designed to separate content from layout: CV data lives in the spreadsheet, rendering configuration lives in 'Quarto', and transformation logic lives in small, reusable R functions. See the 'Typst' typesetting system at < https://typst.app> and the 'Quarto' publishing system at < https://quarto.org>. Inspired by the 'vitae' package < https://CRAN.R-project.org/package=vitae> and the 'Awesome CV' LaTeX template < https://github.com/posquit0/Awesome-CV>.

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.
You can install curriculr from CRAN:
install.packages("curriculr")
Install the development version from GitHub:
# install.packages("pak")
pak::pak("erwinlares/curriculr")
Call create_cv() with no arguments. This copies the template workbook and a
placeholder profile image into your current working directory:
library(curriculr)
create_cv()
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')
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 |
| [email protected] | |
| 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.
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")
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.
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:
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) |
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.
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.
Edit the sections sheet in your workbook. Reorder rows to reorder sections.
Delete a row to remove a 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.
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. NULL triggers scaffold mode.photo — path to the profile image. NULL renders without a photo.output_file — PDF filename. Defaults to "CV.pdf".overwrite — whether to overwrite existing files. Defaults to FALSE.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.
cv_section()Returns a raw Typst string for a section heading. Useful when building custom Quarto templates.
resolve_date_fun()Maps a date_fun token string to an R date formatting function. Useful when
building custom rendering logic outside the standard template.
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.
| Situation | startMonth | startYear | endMonth | endYear |
|---|---|---|---|---|
| Ongoing role | Jan | 2018 | present | (blank) |
| Completed role | Mar | 2015 | Dec | 2022 |
| Single-year event | (blank) | 2021 | (blank) | (blank) |
| No date | (blank) | (blank) | (blank) | (blank) |
curriculr is one of three sibling packages:
curriculr was inspired by two prior projects:
MIT (c) Erwin Lares