A modern, clean resume template built with Typst that integrates seamlessly with the JsonResume standard. This template is based on the excellent basic-typst-resume-template by stuxf, enhanced with JsonResume compatibility and multilingual support.
- JsonResume Integration: Uses the standard JsonResume format for data
- Multilingual Support: Built-in support for Italian and English (easily extensible)
- Clean Design: Professional, ATS-friendly layout
- Modular Architecture: Separate layout logic from data handling
- Customizable Sections: Show/hide sections as needed
- Unbreakable Headers: Section titles stay with their content
- Modern Typography: Beautiful typography with customizable fonts and colors
-
Clone the repository
git clone https://github.com/fruggiero/typst-jsonresume-cv.git cd typst-jsonresume-cv -
Install dependencies (optional, for automation)
npm install
-
Edit your resume data
- Update
resumes/resume-en.jsonorresumes/resume-it.jsonwith your information following the JsonResume schema - You can create new JSON files in the
resumes/folder for other versions/languages.
- Update
-
Customize the template (optional)
- Edit
basic-resume/resume.typto show/hide sections or adjust settings - Modify
basic-resume/base.typfor layout changes
- Edit
-
Generate your resume
# Generate Italian version (resumes/resume-it.json) npm run build:it # Generate English version (resumes/resume-en.json) npm run build:en
The generated PDFs will be available in the
output/folder.
cv/
βββ basic-resume/ # Typst templates
β βββ base.typ # Layout and styling logic
β βββ resume.typ # Data integration and composition
βββ resumes/ # Resume data files
β βββ resume-en.json # English resume data
β βββ resume-it.json # Italian resume data
βββ output/ # Generated PDFs (gitignored)
βββ index.js # Node.js build script
βββ package.json # Dependencies and scripts
βββ README.md
base.typ: Contains all layout, styling, and presentation logic. No dependencies on JsonResume format.resume.typ: Handles data extraction from the temporary data file and composes the final document. Allows easy customization.resumes/*.json: Standard JsonResume data files for different languages/versions.
- The build script (
index.js) accepts a source JSON file from theresumes/folder. - It copies the source data to
output/resume-data.json. - It invokes Typst to compile
basic-resume/resume.typ. resume.typreads the data from../output/resume-data.json.- The final PDF is generated in the
output/folder.
section_with_header: Ensures section titles stay with their first content item (unbreakable)build_section: Generic section builder that handles empty sections gracefullyformatDateRange: Intelligent date range formatting with multilingual supportgetProfile: Helper function to extract social media profiles from JsonResume data- Multilingual sections: Automatic translation of section titles via
get_section_title - Date formatting: Smart handling of ongoing positions, single dates, and date ranges
- Modular sections: Work, Education, Projects, Skills, Certifications, Interests
- Cumulative sections: Skills, certifications, and interests can be combined in one section
Edit the configuration variables in resume.typ:
// Configure visibility of sections
#let show_work = true
#let show_projects = true
#let show_education = true
#let show_cert_skills_interests = trueSet your language in resume.json:
{
"meta": {
"language": "it"
}
}Customize colors, fonts, and layout in the resume.with() call:
#show: resume.with(
author: name,
location: address,
email: emailAddress,
language: lang,
accent-color: "#2D6A4F", // Change accent color
font: "Garamond", // Change font family
paper: "a4", // Paper size
// ...other parameters...
)- Add translations to the
section_titlesdictionary inbase.typ:
#let section_titles = (
work: (it: "Esperienza Lavorativa", en: "Work Experience", fr: "ExpΓ©rience"),
education: (it: "Istruzione", en: "Education", fr: "Formation"),
// ...existing sections...
)- Add month names to
getMonthNames()function:
#let getMonthNames(lang) = {
if lang == "it" {
// ...existing Italian months...
} else if lang == "fr" {
return (
"01": "Jan", "02": "FΓ©v", "03": "Mar", "04": "Avr",
// ...other French months...
)
} else {
// ...existing English months...
}
}The project includes multiple automation options:
Use the included Node.js script for automated PDF generation:
npm start
# or
node index.jsThis script compiles the Typst document and handles any errors gracefully.
This template supports the full JsonResume schema. Key supported sections:
- basics: Name, contact information, profiles (GitHub, LinkedIn, etc.)
- work: Work experience with highlights and location
- education: Educational background with degrees and institutions
- projects: Personal/professional projects with roles and URLs
- skills: Technical skills organized by category with keywords
- certificates: Certifications and licenses
- interests: Personal interests and hobbies
{
"meta": {
"language": "en"
},
"basics": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"location": {
"city": "San Francisco",
"region": "CA"
},
"profiles": [
{
"network": "GitHub",
"url": "github.com/johndoe"
}
]
},
"work": [
{
"name": "Company Name",
"position": "Software Engineer",
"startDate": "2020-01",
"endDate": "2023-12",
"highlights": [
"Led development of key features",
"Improved system performance by 30%"
]
}
]
}#import "base.typ": *
#let r = json("resume.json")
#let lang = r.meta.language
#show: resume.with(
author: r.basics.name,
location: r.basics.location.city + ", " + r.basics.location.region,
email: r.basics.email,
language: lang,
accent-color: "#2D6A4F"
)
// Sections are automatically included based on show_* variables// In resume.typ body parameter:
body: [
// Sezione Esperienza Lavorativa
#if show_work and r.work != none and r.work.len() > 0 {
#work(work: r.work, lang: lang)
}
// Sezione Progetti
#if show_projects and r.projects != none and r.projects.len() > 0 {
#projects(projects: r.projects, lang: lang)
}
// Sezione Istruzione
#if show_education and r.education != none and r.education.len() > 0 {
#edu(education: r.education, lang: lang)
}
// Sezione Certificazioni, Competenze, Interessi
#if show_cert_skills_interests {
#cumulativeCertSkillsInterests(
certifications: r.certificates,
skills: r.skills,
interests: r.interests,
lang: lang
)
}
]- Create a section builder function in
base.typ:
#let getCustomPart(item, lang) = {
generic-two-by-two(
top-left: strong(item.title),
top-right: strong(item.date),
bottom-left: emph(item.description),
)
}
#let custom(items: (), lang: "") = {
build_section("custom", items, getCustomPart, lang)
}- Add translations for the new section:
#let section_titles = (
// ...existing sections...
custom: (it: "Sezione Personalizzata", en: "Custom Section"),
)The template includes intelligent date formatting that handles:
- Single dates
- Date ranges
- Ongoing positions (shows "Present" or "Attuale")
- Same start/end dates
- Large JsonResume files: The template efficiently handles large resume files
- Multiple languages: Switch languages by changing the
meta.languagefield - PDF optimization: Generated PDFs are optimized for ATS systems
- Original Template: basic-typst-resume-template by stuxf
- Data Standard: JsonResume community
- Typesetting Engine: Typst team
- Icons: Science Icons for ORCID support
This project maintains the same license as the original template. Please check the original repository for license details.
Contributions are welcome! Feel free to:
- Add new languages: Extend the multilingual support
- Improve layouts: Enhance the visual design
- Add features: New section types, better date handling, etc.
- Fix bugs: Report and fix any issues
- Documentation: Improve examples and guides
- Install Typst
- Clone the repository
- Make your changes to
base.typorresume.typ - Test with
typst compile basic-resume/resume.typ - Submit a pull request
"Cannot loop over content" error
- Check that arrays are properly formed in
cumulativeCertSkillsInterests - Ensure
skills_blocksis an array, not content - Verify you're using the correct syntax for building arrays with filter operations
"Type content has no method filter" error
- This occurs when trying to filter content blocks instead of arrays
- Use proper array construction:
(item1, item2).filter(x => x != none) - Ensure spread operators are used correctly in content blocks
Missing translations
- Add missing language keys to
section_titles - Verify
meta.languageis set correctly inresume.json
Date formatting issues
- Ensure dates follow
YYYY-MMorYYYY-MM-DDformat - Check for typos in date fields
PDF not generating
- Verify Typst installation:
typst --version - Check for syntax errors in
.typfiles - Ensure
resume.jsonis valid JSON
If you encounter issues:
- Check the documentation: Typst Docs and JsonResume Schema
- Review examples: Look at the provided
resume.jsonexample - Search issues: Check existing GitHub issues
- Create an issue: Provide minimal reproduction steps
Happy resume building! π―
Create beautiful, professional resumes with the power of Typst and the flexibility of JsonResume.
