# How to Add an AI Resume Builder to Your App (Copy-Paste Prompts Included)

- Tool: App Features
- Last updated: July 2026

## TL;DR

An AI resume builder needs five pieces: a multi-step input wizard, a Supabase data model for resume sections, an Edge Function calling claude-haiku-4-5 to polish bullet points, a live PDF preview via @react-pdf/renderer, and a client-side ATS keyword scorer. With Lovable or V0 you can ship a working builder in 6–10 hours for $0–$5/month at launch. Costs stay under $35/month until you reach 1,000 active users.

## What an AI Resume Builder Feature Actually Is

An AI resume builder is a multi-step web form that collects a user's career history and produces a professionally formatted, downloadable PDF resume — with AI improvement available on every text section. The feature combines three concerns that are each solved separately: structured data collection (react-hook-form multi-step wizard), AI content enhancement (Anthropic claude-haiku-4-5 rewriting raw bullet points into ATS-optimised language via Supabase Edge Functions), and document rendering (@react-pdf/renderer generating PDFs in the browser without a server round-trip). The real product decisions are how many template designs to ship at launch, whether AI enhancement shows a diff for user approval or replaces text immediately, and whether users can maintain multiple resume versions for different job applications.

## Anatomy of the Feature

Six components — AI tools generate four of them reliably on the first prompt. The PDF preview renderer and the autosave upsert logic are where first builds reliably break.

- **Multi-step form wizard** (ui): React component using react-hook-form + zod for per-step validation. Step state managed in React context so users can navigate back without losing data. shadcn/ui Progress bar + custom Stepper shows current position. Steps: personal info, work experience (repeating section), education, skills, certifications, summary. Autosaves to Supabase every 30 seconds via upsert.
- **Resume data model** (data): Supabase `resumes` table storing each section as jsonb columns: personal_info, experiences (array), education (array), skills (array), certifications, languages. Supports multiple resume versions per user via separate rows. Row-level security restricts all reads and writes to the authenticated owner.
- **AI content enhancer** (backend): Supabase Edge Function (Deno) that accepts the raw text of a single resume section plus the section type (experience_bullet, summary, skills_list). Calls Anthropic claude-haiku-4-5 with a system prompt instructing it to rewrite the content as ATS-optimised professional resume language — strong action verbs, quantified outcomes, industry keywords. Streams result back to the browser.
- **Resume preview renderer** (ui): React component using @react-pdf/renderer that generates a live PDF preview in an iframe inside the browser — no server round-trip required. Switches between 3 template designs via a template_id prop that applies different React PDF stylesheet objects. Preview updates are debounced to 800ms after the last keystroke to avoid the expensive re-render on every character.
- **PDF export** (service): @react-pdf/renderer PDFDownloadLink component renders the final PDF and triggers a browser download when clicked. Generates the same document the preview renders — same React PDF component, same data. Page size selectable: A4 (European standard) or US Letter.
- **ATS score indicator** (ui): Client-side JavaScript component that counts keyword density across the resume content against a curated list of ATS-important terms per job category (technology, marketing, finance, etc.). Displays a 0–100 score with a breakdown: strong verbs count, quantified achievements count, keyword match rate. No external API — pure regex analysis runs in under 50ms.

## Data model

Two tables: resumes stores all resume section data as jsonb with autosave support; resume_templates is a public read-only catalogue of available designs. Run this in the Supabase SQL editor (Dashboard → SQL Editor → New query).

```sql
create table public.resume_templates (
  id uuid primary key default gen_random_uuid(),
  name text not null,
  thumbnail_url text,
  css_class text not null,
  is_active boolean not null default true,
  created_at timestamptz not null default now()
);

create table public.resumes (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  template_id uuid references public.resume_templates(id),
  name text not null default 'My Resume',
  personal_info jsonb not null default '{}',
  experiences jsonb not null default '[]',
  education jsonb not null default '[]',
  skills jsonb not null default '[]',
  certifications jsonb not null default '[]',
  languages jsonb not null default '[]',
  summary text,
  ats_score int,
  last_ai_enhanced_at timestamptz,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

alter table public.resumes enable row level security;
alter table public.resume_templates enable row level security;

create policy "Users can view own resumes"
  on public.resumes for select
  using (auth.uid() = user_id);

create policy "Users can insert own resumes"
  on public.resumes for insert
  with check (auth.uid() = user_id);

create policy "Users can update own resumes"
  on public.resumes for update
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "Users can delete own resumes"
  on public.resumes for delete
  using (auth.uid() = user_id);

create policy "Templates are public read"
  on public.resume_templates for select
  using (is_active = true);

create index resumes_user_updated_idx
  on public.resumes (user_id, updated_at desc);

create index resumes_user_created_idx
  on public.resumes (user_id, created_at desc);
```

The two indexes keep the 'My Resumes' list fast even when users accumulate many versions. The jsonb default values ('{}' and '[]') ensure every column is safe to read without null checks — important when autosave fires before the user completes step 1.

## Build paths

### Lovable — fit 5/10, 6–8 hours

Best all-round path — Lovable generates the Supabase schema, Edge Function for AI enhancement, multi-step form, and PDF preview in one project with auth already wired. The prompt below is specific enough to avoid the autosave and streaming gotchas.

1. Create a new Lovable project, open the Cloud tab, and confirm Supabase is connected — auth and the database will be provisioned automatically
2. Paste the prompt below into Agent Mode and let it build the full resume wizard, preview, and AI enhancement flow
3. Open Cloud tab → Secrets and add ANTHROPIC_API_KEY so the Edge Function can call claude-haiku-4-5
4. Click Publish → Update; streaming AI enhancement only works on the published domain, not the preview iframe
5. Test PDF export in Chrome and Safari — open the published URL, complete one resume, and download it on both browsers to confirm consistent rendering

Starter prompt:

```
Build an AI resume builder web app with Supabase backend. Schema: two tables — resume_templates (id, name, thumbnail_url, css_class, is_active) with public read policy; resumes (id, user_id, template_id, name, personal_info jsonb, experiences jsonb array, education jsonb array, skills jsonb array, certifications jsonb, languages jsonb, summary text, ats_score int, last_ai_enhanced_at, created_at, updated_at) with RLS so users only read and write their own rows. Use upsert (INSERT ... ON CONFLICT ON CONSTRAINT resumes_pkey DO UPDATE) for every autosave so no duplicate records are created.

UI: Multi-step wizard with 5 steps and a shadcn/ui Progress bar. Step 1: personal info (name, email, phone, location, linkedin_url, portfolio_url, target_job_category dropdown with options: Technology, Marketing, Finance, Sales, Operations, Healthcare). Step 2: Work experience — repeating section, each entry has: company, title, start_date, end_date (or 'Present' checkbox), description textarea. Step 3: Education — repeating section: institution, degree, field, graduation_year. Step 4: Skills (comma-separated tags) and Certifications (repeating: name, issuer, year). Step 5: Review — show all entered data and trigger ATS score calculation.

AI Enhancement: add an 'Improve with AI' button next to every text field and textarea. On click, call a Supabase Edge Function named enhance-resume-section. The Edge Function calls Anthropic claude-haiku-4-5 with system prompt: 'You are an expert resume writer. Rewrite the provided resume section using strong action verbs, quantified outcomes where possible, and ATS-optimised language for the target job category. Return only the improved text, no commentary.' Pass the raw text, section_type (experience_bullet | summary | skills_list), and target_job_category. Stream the result back. Store the original text in a before_ai state variable and show a 'Revert to original' button after enhancement.

PDF Preview: live preview panel using @react-pdf/renderer showing the resume in the selected template design. Debounce preview updates to 800ms after last keystroke. Include a PDFDownloadLink button that generates the final PDF for download. Support 3 templates: Classic (serif, single column), Modern (sans-serif, two columns with accent colour), Minimal (clean whitespace, name as large heading). Page size: A4.

ATS Score: client-side component that analyses the full resume text and returns a score 0–100 based on: percentage of experience bullets starting with action verbs (target: 80%+), number of quantified achievements containing numbers (target: 3+), keyword match rate against a 50-word list for the selected target_job_category. Display score with breakdown and 3 specific improvement tips.

Autosave every 30 seconds using upsert. Handle empty sections by not rendering blank section headers in the preview. Handle AI API errors by showing a toast 'Enhancement failed — your original text is preserved' and keeping the original text visible. Support multiple resumes per user with a 'My Resumes' dashboard listing all resumes newest first.
```

Limitations:

- Streaming AI enhancement does not work in the Lovable preview iframe — always test on the published URL after clicking Publish → Update
- Live PDF preview can flicker if debounce is not set to 800ms — insist on this value in the prompt if Lovable generates a shorter debounce
- @react-pdf/renderer occasionally has rendering differences between Chrome and Safari — test both browsers before launch

### V0 — fit 4/10, 7–10 hours

Best when the resume builder is part of a larger Next.js application. V0 produces cleaner @react-pdf/renderer code and handles AI SDK v6 streaming natively — but requires manual database and environment variable setup.

1. Prompt V0 with the component spec below to generate the wizard, preview, and enhancement components
2. Open the Vars panel and add NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, and ANTHROPIC_API_KEY
3. Run the SQL schema from this page in your Supabase Dashboard SQL editor to create the resumes and resume_templates tables
4. In the V0 Git panel, connect your GitHub repo and push — then deploy to Vercel so the environment variables are injected into serverless functions
5. Test PDF export on the deployed Vercel URL; the V0 sandbox preview cannot render @react-pdf/renderer correctly

Starter prompt:

```
Create a Next.js App Router AI resume builder with the following structure. API route: app/api/enhance-resume/route.ts — POST endpoint that accepts { text, section_type, job_category }, calls Anthropic claude-haiku-4-5 with streaming, and returns a ReadableStream. Use the Anthropic SDK with streaming: createAnthropic(), client.messages.stream(). System prompt: 'Rewrite this resume section using strong action verbs, quantified outcomes, and ATS-optimised language for the job category. Return only the improved text.'

Client components in app/resume-builder/: ResumeWizard — manages 5-step state (personal, experience, education, skills, review) with a shadcn/ui progress bar. Each step is a separate component with react-hook-form + zod validation. Experience and Education steps support repeating entries with add/remove buttons. On every field change, debounce an upsert to Supabase resumes table at 800ms. Use supabaseClient.from('resumes').upsert({ id: resumeId, user_id: userId, ...sectionData }) with the resume id always present to avoid duplicate rows.

AIEnhanceButton — shown beside every textarea; on click streams from /api/enhance-resume and appends tokens to local state. Preserve original text in a useRef. Show 'Revert' button after enhancement completes.

ResumePreview — renders @react-pdf/renderer PDF document with template switching. Three templates: Classic (Times New Roman, single column), Modern (Inter, two columns, blue accent), Minimal (space-mono headings, generous whitespace). Debounce re-render at 800ms. Include PDFDownloadLink button for download. Mark this component dynamic with ssr: false since @react-pdf/renderer is client-only.

ATSScorer — pure client component, no API needed. Analyses resume text client-side: counts action verb starts in experience bullets, counts numeric achievements, matches keywords for the selected job category. Returns score 0–100 with improvement tips. Show in step 5.

Handle: empty sections (skip rendering in preview), AI error state (toast + revert), loading states per step, localStorage not defined guard (use useEffect for any browser API access), Next.js dynamic import for PDFDownloadLink with ssr: false.
```

Limitations:

- V0 does not provision Supabase — you must run the SQL schema manually and add all environment variables in the Vars panel before testing
- PDFDownloadLink must be dynamically imported with ssr: false; V0 sometimes omits this, causing a server-side render crash — add it as a follow-up prompt if needed
- V0 sandbox preview cannot render the PDF component — deploy to Vercel or use the GitHub integration to test PDF export

### Custom — fit 3/10, 2–3 weeks

Justified only when the resume builder needs capabilities that require bespoke engineering: LinkedIn OAuth import, DOCX export with tracked changes, or a semantic job-matching engine that scores the resume against specific job descriptions.

1. LinkedIn OAuth 2.0 integration to pre-fill personal info, work experience, and education from the user's profile — requires a LinkedIn Developer App and server-side OAuth token exchange
2. DOCX export via docxtemplater + PizZip using a real .docx template file with {{variable}} placeholders — gives recruiters an editable Word document, not just a PDF
3. Semantic job matching: embed the resume text with OpenAI text-embedding-3-small, embed a pasted job description, compute cosine similarity via Supabase pgvector, and return a match score with gap analysis
4. Multi-language resume generation: detect the target locale, pass it to the AI enhancement prompt, and apply locale-specific date and currency formatting in the PDF renderer

Limitations:

- LinkedIn OAuth requires LinkedIn Partner Programme approval for profile import at scale — not available instantly
- DOCX rendering fidelity is lower than PDF for complex two-column layouts; test template designs in Word before committing

## Gotchas

- **PDF preview blanks out while the user is typing** — @react-pdf/renderer is computationally expensive — re-rendering a full PDF document on every keystroke blocks the main thread and produces a blank or flickering iframe. Without debouncing, the preview updates 5–10 times per second on fast typists. Fix: Debounce preview updates to 800ms after the last keystroke using a useDebounce hook or lodash debounce. Set the debounce value explicitly in your prompt — AI tools default to 300ms or no debounce, which is too aggressive for PDF rendering. Say '800ms debounce on preview updates' in the prompt.
- **Autosave creates duplicate resume records on every save** — When AI tools generate the autosave logic, they typically write a plain INSERT rather than an upsert. Every 30-second autosave creates a new row in the resumes table, so a user who spends 15 minutes building a resume ends up with 30 rows — one per autosave interval. Fix: The Supabase client upsert requires the primary key to be included in the payload: supabaseClient.from('resumes').upsert({ id: resumeId, user_id: userId, ...data }). Generate the resume id on the client at wizard start (crypto.randomUUID()) and pass it in every upsert call. If you get duplicates, add an ON CONFLICT constraint check to your prompt.
- **Streaming AI enhancement is blank in Lovable preview** — Lovable's preview iframe sandboxes the app and does not support ReadableStream from Supabase Edge Functions. AI enhancement appears to work (the button is clickable, no error shown) but the streamed text never appears — the response body is silently dropped. Fix: Always test AI enhancement on the published URL, not the preview iframe. In Lovable, click Publish → Update after wiring the Edge Function, then open the published domain in a separate browser tab. This is not a bug in your code — it is an iframe security restriction.
- **AI-enhanced text overwrites the user's original with no way to undo** — The default AI tool output replaces the textarea value immediately with enhanced text. If the AI enhancement is worse than the original (hallucinated job title, wrong company name), the user has no recovery path without retyping from memory. Fix: Store the original text in a React useRef or a parallel state variable (e.g., beforeAiText) before streaming starts. Show a 'Revert to original' button as soon as enhancement completes. This is essential UX for an AI writing tool and must be explicit in your prompt.
- **PDFDownloadLink crashes with 'window is not defined' in Next.js** — @react-pdf/renderer accesses browser APIs at module level. When Next.js attempts server-side rendering of a page containing PDFDownloadLink, it crashes immediately with a ReferenceError — no PDF is ever rendered and the page fails to load. Fix: Import PDFDownloadLink and the containing Resume Preview component using Next.js dynamic() with ssr: false. This defers the module load to the browser. In V0-generated code this is sometimes omitted — add a follow-up prompt: 'Wrap the PDFDownloadLink import in dynamic(() => import('./ResumePreview'), { ssr: false })'.

## Best practices

- Store resume section data as jsonb arrays in one row per resume — avoid normalised tables for experiences and education, since resume data is always read and written as a unit
- Generate the resume id on the client at wizard start and pass it in every upsert — this is the only reliable way to prevent autosave duplicate rows
- Cache AI enhancement results by content hash — if the user clicks 'Improve' on the same bullet point twice with identical text, return the cached result instead of making a second API call
- Debounce PDF preview updates to 800ms — @react-pdf/renderer is too expensive to re-render on every keystroke; shorter debounces cause visible flickering on mid-range laptops
- Rate-limit AI enhancement to 10 calls per user per day at launch — power users who iterate heavily generate disproportionate LLM costs before you know your usage patterns
- Preserve original text in state before every AI enhancement call and show a one-click revert — users lose trust in AI tools that overwrite without an undo path
- Keep the ATS scorer client-side with a curated keyword list per job category — a pure-JS implementation is faster, cheaper, and more reliable than an external API call
- Test PDF export in both Chrome and Safari before launch — @react-pdf/renderer has known font rendering differences between browser engines, especially with custom fonts

## Frequently asked questions

### Can I build an AI resume builder without coding?

Yes — Lovable and V0 can generate the full feature from a detailed prompt, including the Supabase database, AI enhancement Edge Function, and PDF export. The prompt on this page is specific enough to produce a working builder in a single session. The main non-coding task is adding your Anthropic API key in the Cloud tab or Vars panel before testing AI enhancement.

### How do I make resumes ATS-friendly automatically?

Two mechanisms work together: the claude-haiku-4-5 enhancement rewrites bullet points with strong action verbs and quantified outcomes that ATS systems scan for; and the client-side ATS score indicator checks keyword density against a curated list for the user's target job category. The score gives users a concrete number to improve before downloading, which drives more enhancement calls and better outcomes.

### Can users have multiple resume versions for different jobs?

Yes, and it's worth building from day one. Store each resume as a separate row in the resumes table (multiple rows per user_id) and show a 'My Resumes' dashboard listing all versions. Users targeting different industries need different keyword emphasis and different template choices — this is a core feature that significantly improves retention.

### What PDF library works best with Next.js for resume generation?

@react-pdf/renderer is the right choice for Next.js App Router. It renders PDF documents as React components in the browser, requires no server round-trip, and integrates cleanly with dynamic imports. The key rule: import PDFDownloadLink with dynamic() and ssr: false, because the library accesses browser APIs at module load and crashes during server-side rendering without this guard.

### How do I add more template designs after launch?

Each template is a separate React PDF stylesheet object that you pass to the ResumeDocument component based on the template_id. Adding a new template means adding a new stylesheet object and a row in the resume_templates table — no database migrations, no schema changes. Design a fourth template in your stylesheet file and insert its record via the Supabase dashboard.

### How do I prevent users from seeing each other's resumes?

Row-level security on the resumes table handles this entirely. The policy 'USING (auth.uid() = user_id)' ensures that every SELECT, INSERT, UPDATE, and DELETE is automatically filtered to the authenticated user's own rows — Supabase enforces this at the database level, not the application level. Even if a user constructs a direct API call with a valid anon key, they can only reach their own data.

### Can I add a cover letter generator alongside the resume builder?

Yes, and it reuses the same architecture. Add a cover_letters table with the same RLS pattern, a separate Edge Function that calls claude-haiku-4-5 with a cover letter system prompt (taking job title, company, and a section of the user's resume as context), and a PDFDownloadLink component using an existing letter template. The AI enhancement infrastructure is identical — a cover letter generator adds roughly 2–3 hours on top of a working resume builder.

### Can the AI import data from LinkedIn to pre-fill the resume?

Not via a simple prompt — LinkedIn's profile API requires OAuth 2.0 authentication and a LinkedIn Developer App with specific permissions (r_liteprofile, r_emailaddress). AI tools cannot wire OAuth provider integrations automatically. This is a custom development task, typically adding 3–5 days to the build, and requires LinkedIn Partner Programme access for full profile data at scale.

---

Source: https://www.rapidevelopers.com/app-features/ai-based-resume-builder
© RapidDev — https://www.rapidevelopers.com/app-features/ai-based-resume-builder
