A giveaway template for a recruiter-facing CV one-pager with a build-time PDF. A bilingual-capable storefront whose single job is to get a recruiter to download your CV PDF. The web page and the PDF render from one content source and never drift.
You fill in three things — your content, your config, optionally your theme — and deploy. Everything else is the reusable frame.
Created by Oleksandr Bilostotskyi. Licensed under Apache-2.0 — you may use it for your own CV and redistribute it, but you must keep the
LICENSEandNOTICEattribution in any redistributed copy of the template itself. SeeNOTICE.
The site ships two renderings of the same dataset at different depths:
- CV page — the web one-pager (summary, one-line experience entries, a curated stack, contacts, languages). Its job is the PDF download.
- CV PDF — the "real CV" a recruiter submits to an ATS. A superset of the page: per-entry highlights and stacks, full skills, education, languages. No photo, structured for ATS parseability.
Both read from src/content/<locale>.ts. The PDF is generated at build time by
scripts/build-pdf.tsx using @react-pdf/renderer — never edited by hand
(ADR-0001). A
parity check fails the build if locales drift structurally. Printing the page
(Ctrl+P) is a separate, shallower path that appends a link to the full PDF.
Routing is config-driven
(ADR-0002): the default locale renders
at /, additional ones at /<code>. Ships with English only.
Discoverability artifacts — robots.txt, sitemap.xml, the JSON-LD graph, the
per-locale OG cards and llms.txt (an llmstxt.org map
for AI crawlers) — all derive from the same config and content, so they never
drift from the CV. llms.txt renders the default locale.
Read CONTEXT.md for the vocabulary first. Then:
Your career data. Every field ships as a structurally-valid placeholder ("Your
Name", one example role) so the build works before you touch it. Replace the
values; keep the shape (enforced by src/content/types.ts). The labels block
is UI chrome (section titles, button text), not personal data.
Sections self-hide when empty (page and PDF): empty an array
(experience: [], projects: [], skills: [], pageStack: [],
languages: []), set summary: '', or drop the education key — that section
simply does not render. name / title / location are always shown. So a
newcomer with no jobs fills projects (pet projects, open-source,
hackathons — each with an optional url to the repo/demo) and leaves
experience: []; the "Work Experience" heading never appears.
Your non-career settings: url, email, pdfBaseName, socials, address,
optional selectedWork footer link, and the localeConfigs list.
Colors and fonts. The JS palette (brand) in theme.ts feeds the PDF and OG
card; the @theme block in globals.css is its mirror for the web page. Change
both, in sync — see DESIGN.md.
- Append an entry to
localeConfigsinsrc/shared/site.tswith four fields:code(ISO 639-1),label(the toggle text — a language name, never a country:УКР, notUK, which reads as Britain),ogLocale(e.g.uk_UA), andogNameLetterSpacing(optical tightening of the name on the OG card — denser scripts like Cyrillic read heavier, so use a tighter value there). The first entry is the default and renders at/; others at/<code>. - Add
src/content/<code>.tsand register it insrc/content/index.ts. - Keep locales structurally parallel — the parity check compares language-independent facts (entry counts, company names, page stack).
- If the script needs extra font subsets (e.g. Cyrillic), add them in
src/shared/fonts.tsand confirm the committed TTFs inassets/fontscover the glyphs (they carry Latin + Cyrillic by default).
Routing, metadata, sitemap, the parity loop, the language toggle and the
per-locale OG cards need nothing more — they all iterate localeConfigs. The
only default-locale artifact is llms.txt (the llmstxt.org spec expects a
single /llms.txt at the site root).
- Next.js 16 (App Router, static export) + React 19
- Tailwind CSS 4
- TypeScript 5
- @react-pdf/renderer 4 — build-time PDF generation
- @vercel/analytics + speed-insights
- pnpm
Requires Node.js 20+ and pnpm.
pnpm install
pnpm dev # http://localhost:3000| Command | Description |
|---|---|
pnpm dev |
Start the Next.js dev server |
pnpm build |
Production build (runs prebuild → PDF generation first) |
pnpm pdf |
Regenerate the CV PDF(s) from content |
pnpm lint |
ESLint |
pnpm format |
Prettier write |
pnpm typecheck |
tsc --noEmit |
pnpm build runs prebuild automatically, which validates content parity and
writes the PDF(s) (named from pdfBaseName) into public/.
src/
app/ Next.js App Router — [[...locale]] route, robots, sitemap, llms.txt
components/ CV page components, download button, locale toggle, JSON-LD
content/ Content source: en.ts (placeholders), index.ts, types.ts
shared/ site config, theme tokens, metadata, fonts, parity, profile
scripts/ build-pdf.tsx + pdf-document.tsx (react-pdf layout)
assets/fonts/ Manrope + JetBrains Mono TTFs (OFL — for PDF/OG glyphs)
docs/adr/ Architecture decision records
Deployed as static HTML (output: 'export'). Every content edit regenerates the
PDF on build, so the download link can never serve a stale CV.
Apache-2.0 © 2026 Oleksandr Bilostotskyi. Attribution required — see
NOTICE. The attribution covers the template; the CV content you
author on top of it is yours.