Resume Generator App: Build the Generator, Keep the HTML
Page one for “resume generator app” is ten copies of the same thing. Template gallery, form, subscription, PDF. Your data lives on their backend and your layout is whatever they picked. There is a fourth option nobody on page one mentions. Describe a resume generator by voice. Get back one HTML file with JSON-driven sections and @media print CSS. No signup. The generator lives on your disk, not their server. Here is how it works and why it is the only version you keep.
A resume generator is three things, not ten
Every product that calls itself a “resume generator app” dresses the same small object in different coats. The object is a JSON blob (your experience), a template function (JSON to HTML), and a print stylesheet (the thing Chrome reads when you hit Cmd+P). Pick any top result on page one and you will find those three pieces underneath the template gallery. The gallery is the sales surface. The three pieces are the app.
The reason this matters for you is leverage. If the generator is a file you can read, you can change it. If the data is a JSON blob at the top, you can version control it. If the print stylesheet is in a @media print block you can see, you can fix the page break that always splits your last bullet in half. Once the three pieces are on your disk, none of the following words apply to your resume anymore: subscription, credit, export limit, template lock-in, data export, account deletion.
The page-one pattern, and the exact gap
These are the brands that dominate the keyword. I checked them. Identical shape across all of them: gallery, form, preview, paywall, PDF. One is open-source but self-host, one is “AI” but still a template gallery with bullet suggestions.
What they all miss, specifically: the generator is a product you can own. Not a subscription. Not a template. A file.
The minimum viable generator, under 80 lines
This is roughly what Quick mode streams back for a starter prompt. No libraries, no build step, no npm. Open the file. Edit the data. Hit Cmd+P. That is the whole loop.
The five pieces every resume generator has to have
Before you prompt, know the parts. This is not for show. If your generated file is missing one of these, it will break on some recruiter’s browser or an ATS parser.
The data blob
A JSON object at the top of the file. Name, contact, summary, experience[], education[], skills[]. Edit it, refresh, done.
The template function
Tagged template strings that interpolate the JSON into semantic HTML. Every heading is h2. Every date is a time element. Every bullet is an li.
The print stylesheet
@page size: Letter, margin 0.5in. A no-print class on the toolbar. break-after: avoid on h2.
Plain-text copy
One button. navigator.clipboard.writeText(document.body.innerText). Paste into the ATS form. Your PDF sits beside it.
No external requests
Use system-ui fonts. No CDN, no fetch, no beacon. Your file opens with airplane mode on and still renders.
How mk0r turns one sentence into that file
Everything below is readable in the repo. Quick mode lives in src/app/api/chat/route.ts and the model choice is a one-line export in src/app/api/chat/model/route.ts. The VM template is pinned in src/core/e2b.ts. No marketing glaze on any of this.
Prompt to generator
The anchor fact nobody will copy
Most pages in this keyword cluster lift each other’s bullet points. The one thing that is hard to fake is a readable source file. The Quick mode model is one line:
The stream is NDJSON, parsed by readNDJSON() in src/app/api/chat/route.ts:113-148, yielding SessionNotification chunks as they arrive. The VM sandbox uses E2B template 2yi5lxazr1abcs2ew6h8 pinned in src/core/e2b.ts:30-32, and the Hero’s voice input comes from useVoiceInput in src/app/(landing)/page.tsx:886. If a competitor claims “instant”, ask them which model, which template, which file. If they cannot point at a line of source, they are selling the word and not the thing.
The step-by-step, written plain
From one spoken sentence to a printed PDF
Open mk0r and hold the mic
No signup gate. The landing-page Hero (src/app/(landing)/page.tsx:886) uses useVoiceInput. Say: "Build a resume generator as one HTML file. Two columns, left narrow with skills and contact, right wide with experience and education. Teal accent. Print CSS for one-page Letter."
Watch the file stream in
Quick mode (FREE_MODEL = "haiku" in src/app/api/chat/model/route.ts:5) streams the HTML through readNDJSON(). The preview iframe updates as tokens arrive. You see the heading, the left column, the @media print block appear in that order.
Replace the sample JSON with yours
The file opens with a sample data object at the top. Replace 'Ada Lovelace' with you. Paste your experience as objects with role, org, years, and bullets. Refresh the file. The template re-renders. No rebuild, no deploy.
Prompt for changes, not templates
"Add a Projects section between Experience and Education." "Make the dates right-aligned and monospace." "Add a clipboard button that copies plain text for ATS." Each prompt returns a new HTML file. The shape of your resume changes. The content stays yours.
Print it and keep the file
Cmd+P. Chrome renders the @media print version. Save as PDF. Commit the HTML to git. Next year, open it, update the JSON, reprint. No subscription renewal. No export credit. The generator is a file on your disk and has been since the first token arrived.
The scheduler line is the one that matters for print
Resume generators live or die on the pagination. Nine out of ten resumes that print badly do it because @page was never set, so Chrome inherits the A4 default and your last Experience bullet slides off page one. The fix is one rule:
The generator file must contain all of these to be usable
- A JSON blob with your name, contact, and experience array
- A template function that maps JSON to HTML via tagged strings
- @media print rules for @page, margins, and page breaks
- Semantic HTML (h1, h2, time) so ATS parsers can read it
- A no-print class on the Print button so it hides on export
- A plain-text copy button for ATS form fields
- Zero external requests when fonts are set to system-ui
Six prompts, six very different generators
Change the prompt, change the app. These are the six shapes that cover 95% of resume needs. Paste any into mk0r.
Resume generator as one HTML file. Single column, standard section headers (Experience, Education, Skills, Certifications), semantic h2 tags, no icons, system fonts. Plain-text copy button.
Resume generator, two columns, narrow left sidebar with contact, skills, languages; wide right column with experience and education. Teal accent border on h2. One page max on Letter.
Resume generator for a staff engineer. Summary section at top (3 lines), then Experience with sub-bullets per role, then Selected Projects, then Education and Talks. Monospace for dates.
Resume generator that also renders a JSON array of case studies as cards below the resume block. Screen view shows both, print view hides the case studies.
CV generator in LaTeX-ish serif, sections for Appointments, Publications, Grants, Teaching, Service. No photo. Multi-page allowed. Page numbers in print footer.
Resume generator that loads resume.md from a fetch('./resume.md'), parses it with a tiny front-matter split, and renders headings and bullets. Print CSS still applies.
Generated HTML vs. template-gallery SaaS
Honest side-by-side. This is the trade you are making.
| Feature | Typical template-gallery SaaS | mk0r (generated HTML) |
|---|---|---|
| What you get back | A PDF rendered on their server | A single HTML file that is the generator |
| Change the section order | Pick a different template from the gallery | One prompt, rewrites the template function |
| Add a non-standard section | Usually impossible without custom work | Prompt: "add a Published Writing section" |
| Print CSS control | Hidden inside their renderer | You own @page, margins, page breaks |
| Account required | Email, often before any preview | None |
| Export cost | Subscription or per-download fee | Cmd+P in your browser, free |
| Version control the resume | Platform history, gone if you leave | git add resume.html |
| Your data lives where | On their SaaS backend | Inside your file, on your disk |
| Voice prompt to regenerate | Click through the builder | Hold mic, say what to change |
Generated HTML vs. Reactive Resume style open source
The second trade. More interesting, because both are ideologically on the same side. The difference is time-to-working and how opinionated the shape is.
| Feature | Open-source builder (one app, many templates) | mk0r (tailor each time) |
|---|---|---|
| Output shape | A React app with preset templates | One HTML file tailored to your prompt |
| Time to working output | Self-host setup or sign up | ~30 seconds, streamed in-browser |
| Customize layout | Write React or fork the repo | Describe it, regenerate |
| Who owns the file | You, if you self-host | You, from the first token |
| Works offline | Needs the app running | Yes, the file runs locally |
| Good for non-developers | Setup steps needed | Yes, you never open a terminal |
“The generator lives on your disk, not their server. That is the whole differentiator.”
Small rules that make the output much better
Thirty seconds is fast. A good thirty seconds is not automatic. These are the half-dozen habits that move the output from “fine” to “this prints cleanly the first time”.
Do describe the shape, not the words
Say "two column, narrow left, sidebar with skills". Leave the bullets to you. The model nails layout given constraints, and you choose your own story.
Do ask for print CSS by name
The magic line is @media print { @page { size: Letter; margin: 0.5in; } }. Prompt for it explicitly. Without it, Chrome paginates unpredictably and you get awkward half-page breaks.
Do name the sections ATS systems expect
Use Experience, Education, Skills, Certifications. Avoid cute headers like "My Journey" or "Wins". ATS parsers are regex-based. They want the words they know.
Do request a plain-text export button
navigator.clipboard.writeText(document.body.innerText) copies a clean version into the clipboard. You paste that into the ATS form, they parse it perfectly, and your PDF shows up beside it.
Do not rely on icons
Icon fonts and SVG symbols confuse ATS parsers and sometimes show as gibberish boxes. If you want visual rhythm, use color and spacing, not glyphs.
Do not embed the resume inside a PDF layer
Generating a canvas-rendered PDF via jsPDF strips the text layer. Stick to print CSS and Cmd+P. The resulting PDF has selectable text, which matters for recruiter searches.
Where VM mode fits (and when to switch)
One HTML file is enough for almost any personal resume. It stops being enough the moment you want a public resume site with multiple pages, a blog alongside it, a contact form that emails you, or a subdomain that serves different resumes per audience. At that point, switch to VM mode. Same prompt box, different backend. You get a real Vite + React + TypeScript + Tailwind v4 project inside an E2B sandbox, pre-baked and ready at port 5173.
The trade-off: you gain routing, components, and a production build, and you give up the “one file on disk” purity. For a plain resume, stay in Quick mode. For a resume site with a portfolio, a stats page, and a /hire endpoint, use VM mode.
The honest limits
A generator produced this way is excellent for personal resumes, CV prototypes, job-board drops, recruiter-visible portfolio pages, and rapid experiments with new section orders. It is not a full design tool. If you want drag-and-drop layout with a WYSIWYG canvas, Canva wins. If you want a perfectly styled PDF with zero input from you, Rezi’s AI bullet generator wins.
What this method replaces is the decade-long status quo where a resume was a thing you rented from a SaaS. Now it is a thirty-second file. What you do with the ten minutes you saved is on you.
Want a walkthrough on shaping your own generator?
I will show you which prompt shape produces which resume shape, live. Book 20 minutes. Bring the job you are applying for.
Book a 20 min walkthroughFrequently asked questions
What is a resume generator app, technically?
A resume generator app is three things in a trench coat: a data blob (your experience as JSON or Markdown), a template function that maps that data to HTML, and a stylesheet with @media print rules that makes it paginate cleanly when you hit Cmd+P. Everything past that is cosmetic. Pick a theme, add a photo, toggle two columns, reorder sections. The core is still JSON in, HTML out, print CSS on top.
Why would I generate my own instead of using Rezi, Resume.io, or Canva?
Three reasons. One, they hold the source. Their servers render the PDF, so your layout is whatever their templates allow and your export is gated by a subscription. Two, they hold your data. If you stop paying, your resume history leaves with them. Three, you cannot embed, fork, version control, or script their output. With a single HTML file, the generator is on your disk. You commit it to git, open it in a browser, and print to PDF any time without asking anyone's permission.
How fast is mk0r at producing a working resume generator?
Quick mode is pinned to Claude Haiku in src/app/api/chat/model/route.ts (export const FREE_MODEL = 'haiku'). It streams HTML token by token through a ReadableStream parsed by readNDJSON() in src/app/api/chat/route.ts. The preview iframe updates as the response arrives, so the first section header is visible in the iframe before the @media print block finishes writing. The landing copy is 'watch it become a real, working app in seconds.'
Can I really describe a resume generator by voice?
Yes. The Hero component at src/app/(landing)/page.tsx:886 uses the useVoiceInput hook. Hold the mic button, say 'a resume generator with a two-column layout, my name at top, skills in a sidebar, print to exactly one page.' The transcript fills the prompt box and submits. No native app, no separate install. Just the browser's getUserMedia and the Whisper-grade transcribe route.
What about ATS? Will a generator I built myself pass the bots?
It will if you prompt for it. Tell mk0r 'single column, standard section headers (Experience, Education, Skills), semantic HTML, no icons, a hidden plain-text export button.' The generator emits headings as h2, dates as time elements, and gives you a button that copies a plain-text version to the clipboard, which is what ATS parsers actually ingest. The reason template-gallery generators feel 'ATS safe' is because they strictly constrain layout. You can apply the same constraints in your prompt.
Do I need to sign up or pay anything?
No. mk0r's landing copy is explicit: no signup wall, no paywall, no install. Open mk0r.com, hold the mic or type, hit send. Quick mode runs on the free Haiku tier. If you want VM mode (a full React + Vite project) you can use that too, on the same flow.
Can I keep editing the resume after the generator is built?
Yes, in two ways. Option one: edit the JSON data blob at the top of the HTML file and refresh the page. Option two: keep prompting mk0r. 'Add a Projects section between Experience and Education with title, year, and a one line description.' The model rewrites the template function to add a new section. The JSON schema gains a projects array. Your existing data stays. That is the moving part of the generator, and it is the part the template galleries hide.
What if I need a React + Vite project instead of one HTML file?
Switch to VM mode. The VM backend spins up E2B template 2yi5lxazr1abcs2ew6h8 (pinned in src/core/e2b.ts:30-32), which is pre-baked with Vite + React + TypeScript + Tailwind v4 and Playwright MCP on port 3001. Boot is roughly 2.5 seconds from the warm pool. You end up with a real component tree: <ResumeData />, <Section />, <PrintLayout />, and a dev server at localhost:5173 inside the sandbox that hot-reloads as you iterate.
Will the HTML file I get back actually print cleanly on one page?
If you prompt for it. The key line is '@media print { @page { size: Letter; margin: 0.5in; } body { font-size: 10pt; } .no-print { display: none; } }'. Ask for that explicitly. Also ask for a one-page-max check: 'wrap the resume in a fixed-height container equal to one page so content that overflows is visible during editing.' When you print, Chrome paginates inside that container and you see immediately whether the resume fits.
What is the difference between mk0r and an open-source builder like Reactive Resume?
Reactive Resume (rxresu.me) is a full React application you self-host or use as SaaS. You pick from its templates, fill its forms, and export. It is high quality. The shape is still 'template gallery plus form'. mk0r generates a new single-file generator each time, tailored to the shape you describe, and the generator lives on your disk. Reactive Resume is a tool you use. mk0r is a tool that writes the tool, in 30 seconds, from a voice prompt.
Does the generated file phone home or leak data?
No. Quick mode output is static HTML, CSS, and vanilla JavaScript. There is no mk0r runtime, no analytics, no fetch to our servers. You can audit it by opening the file, hitting View Source, and searching for 'mk0r' or 'http'. The only network requests are whatever fonts you reference. If you ask for 'no external fonts, system fonts only', there are zero network requests after load.
No signup. No paywall. No install. Just describe the generator you want.