An academic-first Typst package for cover letters and research, teaching, or
personal statements. It shares its design system and profile shape with
modernpro-cv, so a CV, a
letter, and a statement read as one application while each source file remains
self-contained.
The default design is intentionally complete. Most users only need to provide their profile, the recipient, and the letter text.
All people, institutions, positions, projects, and claims in the examples are fictional. Reserved domains and an all-zero ORCID are used deliberately.
Preview
Academic cover letter
Research statement
Choose a document
| Document | Start from | Best for |
|---|---|---|
| Cover letter | coverletter.typ |
A role, fellowship, grant, or programme application addressed to a recipient |
| Statement | statement.typ |
Research, teaching, diversity, service, or personal statements with section headings |
Both documents keep an editable profile dictionary at the top of their own
file. Copy the same values when you want matching headers, without adding a
shared-file dependency.
Quick start
Create and compile a project with the Typst CLI:
typst init @preview/modernpro-coverletter:1.0.0
cd modernpro-coverletter
typst compile coverletter.typ
typst compile statement.typ
The generated project contains:
modernpro-coverletter/
├── coverletter.typ <- recipient, letter body, and closing
└── statement.typ <- title, headings, and statement body
Use typst watch coverletter.typ or typst watch statement.typ for automatic
recompilation while editing.
First edit checklist
- Replace the placeholder identity at the top of the document you are using.
- In
coverletter.typ, replace every recipient field and write the letter in short block paragraphs. - In
statement.typ, rename the title and headings to match the requested document. - Remove example claims rather than adapting them as if they were factual.
- Compile to PDF and inspect the header, first body line, closing, and page breaks.
Minimal academic cover letter
Keep your identity beside the letter content in the same source file:
#import "@preview/modernpro-coverletter:1.0.0": *
#let profile = (
name: [Dr. Maya Chen],
role: [Lecturer in Computational Social Science],
address: [Edinburgh, United Kingdom],
contacts: (
(text: [maya\@northbridge.example], link: "mailto:maya@northbridge.example"),
(text: [maya.example.org], link: "https://maya.example.org"),
(text: [ORCID~0000-0000-0000-0000], link: "https://orcid.org/0000-0000-0000-0000"),
),
)
#show: coverletter.with(
profile: profile,
recipient: (
name: [Professor Eleanor Hughes],
role: [Chair, Faculty Search Committee],
department: [School of Public Policy],
organization: [Eastborough University],
address: [London, United Kingdom],
date: [9 July 2026],
subject: [Application for Lecturer in Digital Governance],
greeting: [Dear Professor Hughes and Members of the Committee,],
),
)
Write the opening paragraph here.
Write the research, teaching, or professional case in short block paragraphs.
Close by explaining the fit and thanking the committee.
The default closing is Sincerely, followed by the profile name. Override it
only when needed:
closing: (
salutation: [Best regards,],
supplements: ([Enclosure: Curriculum vitae],),
)
The three settings
Everything beyond profile and recipient is optional:
| Setting | Values | Purpose |
|---|---|---|
profile |
name, optional role, address, contacts |
Who you are |
preset |
"compact", "default", "relaxed" |
The whole vertical rhythm |
accent |
any colour | The one colour in the document |
#show: coverletter.with(
profile: profile,
recipient: (name: [Recipient Name],),
preset: "compact",
accent: rgb("#1e3a5f"),
)
A preset coordinates header rows, recipient and subject spacing, title spacing, heading-to-body gaps, line spacing, and paragraph spacing at once. Choose a preset rather than tuning gaps individually.
Recipient fields
The recipient group uses ordinary letter terminology:
| Field | Purpose |
|---|---|
name |
Recipient or committee chair |
role |
Role or position |
department |
Department, school, or unit |
organization |
University, company, or institution |
address |
City or postal address |
postcode |
Optional separate postcode line |
date |
Letter date; omit it to use today’s date |
subject |
Sentence-case application subject |
greeting |
Opening greeting |
Every recipient field is optional. If date is omitted, the template inserts
today’s date; other empty fields leave no placeholder gaps. For an application,
provide at least the recipient or committee, organization, subject, and
greeting whenever they are known.
Statement template
statement uses the same profile, header, and visual identity. First- and
second-level Typst headings are styled automatically, so a research statement
stays easy to edit:
#import "@preview/modernpro-coverletter:1.0.0": *
#let profile = (
name: [Dr. Maya Chen],
role: [Lecturer in Computational Social Science],
address: [Edinburgh, United Kingdom],
contacts: (
(text: [maya\@northbridge.example], link: "mailto:maya@northbridge.example"),
(text: [maya.example.org], link: "https://maya.example.org"),
),
)
#show: statement.with(
profile: profile,
title: [Research Statement],
)
= Research agenda
Introduce the question that connects your work.
= Current programme
Describe your strongest projects, methods, and contributions.
= Future work
Set out the next phase of the programme.
Use level-one headings (= Heading) for the major argument. Prefer three to
five descriptive sections over many short fragments. Statements add a compact
continuation header from page 2 by default.
Keep every document self-contained
The profile dictionary has the same shape as modernpro-cv, but each generated document keeps its own copy. An application folder therefore has no shared personal-data dependency:
application/
├── cv.typ
├── coverletter.typ
└── research-statement.typ
Copy the small #let profile = (...) block when starting another document.
This deliberate duplication makes every file portable and independently
compilable; update the copied values only when the application needs a
different role or contact order.
Shared academic design
The CV, cover letter, and statement use the same tokens.
| Family | PT Serif, falling back to Libertinus Serif — one family, two weights |
| Sizes | 8.8pt captions · 9.8pt recipient and address · 10.8pt body · 15pt statement title · 18pt name |
| Colours | #1f2933 ink · #667085 muted · #1e3a5f accent · #dde3ea rules |
| Margins | 2.2cm left and right · fixed 2cm top, matching modernpro-cv |
| Header | Split grid: name, role, and location left; stacked contacts right; accent rule below |
| Paragraph style | Left aligned, no first-line indent, block paragraphs |
Because the left and right margins match the CV exactly, the header rule lands at the same position in every document of the application.
Block paragraphs are easier to scan than justified text carrying both an indent and a blank gap. The default starter also avoids decorative icons, keeping text extraction and accessibility clean.
Advanced configuration
Most documents never need this section. When you do need a specific override, optional settings are grouped by purpose:
| Group | Use it for |
|---|---|
theme |
Fonts, semantic colours, sizes, and weights |
layout |
Margins, paragraph rhythm, header layout, and continuation behaviour |
closing |
Sign-off, signature spacing, and enclosures |
The semantic theme keys match modernpro-cv:
#let theme = (
font: "PT Serif",
text: rgb("#1f2933"),
heading: rgb("#1f2933"),
muted: rgb("#667085"),
accent: rgb("#1e3a5f"),
rule: rgb("#dde3ea"),
)
Common layout keys are:
| Key | Purpose | Default |
|---|---|---|
preset |
Coordinated document rhythm: "compact", "default", or "relaxed" |
"default" |
margin |
Page margins | Academic margins above |
first-line-indent |
Paragraph indent | 0em |
line-spacing |
Typst paragraph leading | 0.72em at the default preset |
paragraph-spacing |
Space between paragraphs | 1.35em at the default preset |
justify |
Fully justify body text | false |
header-style |
"split" or "centered" |
"split" |
contact-layout |
"stacked" or "inline" contacts in a split header |
"stacked" |
repeat-header |
Add a compact continuation header from page 2 | cover letter: false; statement: true |
page-numbering |
Add current and total page count to continuation headers | true |
header-height |
First-page identity area height | 17mm |
contact-separator |
Separator between contact items | " · " |
date-format |
Format for an automatic date | [day] [month repr:long] [year] |
The full identity header is always part of the first page’s document flow. Its
position does not move when continuation behaviour changes. If repeat-header
is enabled, later pages receive only a compact name, document label, and page
count; contact details are not repeated or placed outside the accessible first
page structure. Statements enable this behaviour by default.
Contacts
A contact may be linked, unlinked, or plain content, and may carry an optional
icon exactly as in modernpro-cv:
contacts: (
(text: [you\@example.edu], link: "mailto:you@example.edu"),
(text: [your-site.example], link: "https://your-site.example"),
[ORCID 0000-0000-0000-0000],
)
Escape @ as \@ inside Typst content. Two or three concise academic contacts
usually fit best. FontAwesome remains usable as an opt-in content choice, but it
is not required by the package or starter.
Common recipes
Add enclosures
closing: (
supplements: (
[Enclosure: Curriculum vitae],
[Enclosure: Research statement],
),
)
Use a centered header
layout: (
header-style: "centered",
contact-layout: "inline",
)
Disable a statement continuation header
#show: statement.with(
profile: profile,
title: [Research Statement],
layout: (repeat-header: false),
)
Use an explicit date
recipient: (
date: [9 July 2026],
)
Legacy compatibility
Nothing was removed. The flat parameters font-type, name, address,
contacts, the colour parameters (primary-colour, headings-colour,
subheadings-colour, date-colour, link-colour), the type sizes, and the
spacing parameters all still resolve. layout: (density: ...) remains an
accepted spelling of preset.
The recipient aliases start-title and cl-title still map to greeting and
subject. institution remains an alias for organization, and position
remains an alias for role. The legacy salutation argument controls the
closing sign-off.
New documents should use profile, recipient, and the three settings shown in
the quick start.
Upgrading from 0.0.x
Your existing documents keep compiling, and they will change more than a visual
redesign alone would explain. In 0.0.x the body style was applied inside a
helper function that produced no content, so Typst scoped the rules to that
helper and discarded them: letters rendered at Typst’s 11pt default rather than
the configured body-size, and line-spacing, paragraph-spacing, justify,
first-line-indent, and link-colour had no effect at all. Those settings now
work. If you had compensated for the old behaviour with manual overrides,
remove them.
Troubleshooting
- The email causes a syntax error: escape
@as\@inside Typst content. - The first page looks crowded: shorten the contact block and recipient address before selecting the compact preset.
- A statement heading is too close to a page break: keep it as a Typst heading; the template marks styled headings as sticky.
- The letter unexpectedly becomes two pages: remove repetition first, then
try
preset: "compact". - A font is unavailable: set
theme: (font: "Libertinus Serif").
Local development
Compile the repository examples against the working template:
typst compile example-coverletter.typ
typst compile example-statement.typ
All people, institutions, positions, projects, and claims in the examples are fictional.
License
This template is released under the MIT License. See LICENSE.

