✦ Building Your Own Resume in Typst
I decided to update my resume today, just for fun. I originally wrote it in Overleaf, the compiler, but for a while I’ve been meaning to rewrite it on my own system so I can easily access it without an internet connection and be able to check it in to version-control.
When looking for other ways to create my resume, varying from JSON resume, to RenderCV, I couldn’t settle on one. I wanted immense customizability in order to have anything be possible, and turn it into yet another Jake’s Resume clone.
And then I stumbled upon this article, introducing me to Typst, a modern system for typesetting documents. So, I decided to rewrite my whole resume in it in a day.
Trying Typst
My first impression of Typst was that it felt much more approachable than . It still gives you the sense that you can control everything, but the syntax is much less intimidating, and when I don’t know something, their docs were great to read.
It is also very fast. Being able to make a change and see the result almost immediately made it much easier to experiment with spacing, layout and little stylistic details. It was great to run the typst watch command and watch the document update in real time.
Another thing I liked was that Typst sits in a nice middle ground between markup and programming. You can write text naturally:
= Experience
I worked on *backend systems*, developer tooling, and internal platforms.
but when you want reusable pieces, those are easy to define too. For example, if I wanted a small helper from section headings or resume entries, I could write a function instead of repeating formatting everywhere:
#let resume-item(title, date, body) = [
*#title* \h(1fr) #date
#body
]
#resume-item(
"Software Engineer"
"2023-2025"
[Worked on APIs, CI tooling, and infrastructure.]
)
It also makes global styling pleasantly straightforward. One feature I thought was especially elegant was Typst’s show rules. They let you redefine how a kind of element should render throughout the document. For example, I can restyle every link like this:
#show link: item => box[
#set text(blue)
#underline(item)
]
It was great to define a rule on an object and now every instance of that object picks it up automatically.
Building the resume
Once I got comfortable with the basics, the resume itself ended up being a handful of small reusable pieces.
For example, I defined a helper for section headings so every section looked consistent:
#let resume-section(title) = [
#set text(12pt, weight: "bold")
#title
#line(length: 100%)
#v(0.6em)
]
Then I could use it throughout the document without repeating the styling:
#resume-section("Experience")
#resume-section("Projects")
#resume-section("Education")
I also ended up making a small helper for contact information at the top of the page, where I wanted a row of links seperated by dots:
#let contact-item(label, url) = link(url)[#label]
#let contact-row(items) = [
#for (index, item) in items.enumerate() [
#item
#if index < items.len() - 1 [ • ]
]
]
That let me write the header much more cleanly:
contact-row((
contact-item("GitHub", "https://github.com/example"),
contact-item("Email", "mailto:me@example.com"),
contact-item("Site", "https://example.com"),
))
The final resume.typ file still reads mostly like a document, but it has just enough structure that I can reuse pieces, tweak the layout, and make global style changes without too much effort.
Overall, Typst was a lot of fun to use. I started this mostly because I wanted a resume I could edit locally and keep in version control, but I ended up finding a tool I’d happily use again for other documents.
Oh, and the best part? It outputs natively to SVG.