# CV Editor React Component — README A reusable, plug-and-play React component that provides a CV/resume builder with: * a form-based editor (stepper UI), * a rich-text **Tiptap** summary editor, * live preview (inline) and iframe-isolated printable preview (A4), * structured JSON CV model (single source of truth), * easy integration points for saving, exporting (server-side Puppeteer example), theming and templates. This README explains how to install, integrate, customize, test and deploy the component in an existing React app or run it standalone. --- # Table of contents 1. What this component does 2. Key features & design decisions 3. Quick start (install + run) 4. Component API (props & events) 5. File / folder structure (suggested) 6. How to plug into an existing React app (examples) 7. Templates: how they work & how to add more 8. Exporting PDFs (server-side Puppeteer example) 9. Security & sanitization (DOMPurify) 10. State management recommendations (Zustand + TanStack) 11. Styling and theming (Tailwind / CSS isolation) 12. Accessibility checklist 13. Testing & CI suggestions 14. Troubleshooting / common errors 15. Next steps & extension ideas --- # 1. What this component does This component provides a full CV editor UI you can drop into your React app. It stores the CV as a structured JSON model and renders templates from that model. The summary field is a rich editor (Tiptap) that stores content as HTML/JSON; previews are rendered inline and inside an iframe (print-ready HTML). For production PDF export you can reuse the same printable HTML on the server and render it with Puppeteer. --- # 2. Key features & design decisions * **Single source of truth**: the CV is a JSON object (easy to save, modify, export). * **Templates as renderers**: templates are modular React components / HTML + CSS that accept the CV JSON and return DOM or printable HTML. * **Rich summary**: Tiptap (ProseMirror) used for structured, extensible rich text with predictable HTML output. * **Preview parity**: inline preview uses the same React template rendering to reduce export surprises; iframe preview isolates CSS for print fidelity. * **Server-side parity**: printable HTML builder can be used on server in Puppeteer to generate pixel-perfect PDFs. --- # 3. Quick start (install + run) ### Minimal dependencies Add these packages to your project: ```bash # Core npm install react react-dom # UI + styling (suggested) npm install tailwindcss # Rich text npm install @tiptap/react @tiptap/starter-kit @tiptap/extension-placeholder # Optional & recommended npm install dompurify npm install axios npm install zustand @tanstack/react-query ``` ### Example package.json dev/start scripts ```json { "scripts": { "start": "vite", // or react-scripts start / next dev "build": "vite build", "test": "vitest" } } ``` ### Run Add the component file (e.g. `CVEditorWireframe.jsx`) to your app and import it into a page. Start your app as usual (`npm start` / `npm run dev`). --- # 4. Component API (props & events) Use this component as ``. Example usage: ```jsx import CVEditor from './components/CVEditorWireframe' function App() { const initialCV = { /* optional initial JSON model */ } async function handleSave(cvJson) { // e.g. POST /api/cv await axios.post('/api/cv', cvJson) } return ( ) } ``` ### Props * `initialCV` — `{}` CV JSON model to pre-populate the editor (optional). * `templates` — `[{ id, name, renderer }]` list of templates. `renderer` can be a React component that accepts `{cv}` or a function that returns printable HTML. * `onSave(cv)` — async callback called when user saves (or autosave triggers). * `onExportRequest(cv)` — callback to call when user requests PDF export. If provided, component can call it (you can send to server for Puppeteer). * `exportEndpoint` — optional server endpoint URL to POST CV JSON for export. * `allowDownload` — `boolean`, whether to show Export/Download controls. * `theme` — `'light' | 'dark'` (optional, used to add top-level class). * `className` — additional CSS class for outer container. * `onChange(cv)` — optional callback fired on any CV change (useful for autosave). --- # 5. File / folder structure (suggested) ``` src/ components/ CVEditorWireframe.jsx // main component (editor + preview) templates/ TemplateClassic.jsx TemplateATS.jsx editors/ RichSummaryEditor.jsx // Tiptap wrapper utils/ printableHtml.js // buildPrintableHtml(cv) escapeHtml.js hooks/ useCvAutosave.js pages/ EditorPage.jsx // where you mount CVEditor server/ export/ exportPdf.js // Express + Puppeteer server code (optional) ``` --- # 6. How to plug into an existing React app ### A. As a child component (recommended) 1. Copy `CVEditorWireframe.jsx` and its dependencies into your project. 2. Import and render it inside an existing route or component. 3. Provide `initialCV`, `onSave` and `templates` props as needed. ```jsx // routes/ProfileEditor.jsx import CVEditor from '../components/CVEditorWireframe' import TemplateClassic from '../components/templates/TemplateClassic' const templates = [ { id: 'classic', name: 'Classic', renderer: TemplateClassic }, // add ATS or other templates ] export default function ProfileEditor() { const initialCV = {...} async function handleSave(cv) { await api.saveCv(cv) } return } ``` ### B. As an isolated micro-frontend If you prefer it to be self-contained: bundle the component into its own package or iframe it from a separate deployment. But prefer simple import for easier code sharing and debugging. --- # 7. Templates: how they work & how to add more **Two options for templates:** 1. **React component template** — a React component that accepts `{ cv }` and renders markup. Use for inline preview and server rendering (with `renderToString`). 2. **HTML template function** — a function that receives `cv` and returns printable HTML string. Use for server-side export or when you want designers to provide standalone HTML/CSS files. **Example React template** ```jsx export default function TemplateClassic({ cv }) { return (

{cv.personal.firstName} {cv.personal.lastName}

{cv.work.map(w => (

{w.title}

{w.company} • {w.period}
    {w.bullets.map((b,i) =>
  • {b}
  • )}
))}
); } ``` **Guidelines** * Provide both `visual` templates (fancier CSS) and `ATS` templates (semantic, simple markup). * Keep template CSS isolated (CSS modules, scoped styles or render in iframe). * Use the same rendering logic on the server for PDFs to ensure parity. --- # 8. Exporting PDFs (server-side Puppeteer example) ### Why server-side? Puppeteer produces consistent, pixel-perfect PDFs (server or worker) and you can offload CPU work off the client. ### Minimal Express endpoint (example) ```js // server/export/exportPdf.js import express from 'express'; import puppeteer from 'puppeteer'; const router = express.Router(); router.post('/export/pdf', async (req, res) => { try { const cv = req.body; // reuse printable-html builder (same logic as client) const html = buildPrintableHtml(cv); const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); await page.setContent(html, { waitUntil: 'networkidle0' }); const pdfBuffer = await page.pdf({ format: 'A4', printBackground: true }); await browser.close(); res.set({ 'Content-Type': 'application/pdf', 'Content-Disposition': `attachment; filename="${cv.personal.firstName}_${cv.personal.lastName}_CV.pdf"` }); res.send(pdfBuffer); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to create PDF' }); } }); export default router; ``` **Notes** * Use a worker or job queue (BullMQ) for high volume. * Reuse the same HTML builder (`buildPrintableHtml`) on the server (move the builder to a shared package or duplicate carefully). * If using serverless (Vercel / Netlify), be aware Puppeteer needs special setup (use `chrome-aws-lambda`). --- # 9. Security & sanitization (DOMPurify) Because summary HTML comes from users, always sanitize before rendering into `srcDoc` or sending to server. ```bash npm install dompurify ``` ```js import DOMPurify from 'dompurify' const safeHtml = DOMPurify.sanitize(cv.summary)
``` Sanitize on both client and server if the server stores the HTML or returns it to other users. --- # 10. State management recommendations * **Zustand** — simple local/global client state (editor draft, UI toggles). * **TanStack Query** — server-state (fetching/saving CVs, export job status). * Use both: keep in-memory editing state in Zustand, and use React Query for network operations (cache, optimistic updates, retries). Example: ```js // useCvAutosave.js import { useMutation } from '@tanstack/react-query'; export function useAutosave() { return useMutation(cv => api.saveCv(cv), { /* ... */ }) } ``` --- # 11. Styling & theming * Use **Tailwind CSS** for fast iteration: the wireframe uses tailwind utility classes. * For templates, prefer scoped CSS (CSS modules) or provide a `template.css` imported only by the template to avoid leakage. * For iframe preview, `srcDoc` includes template CSS directly—this isolates styles perfectly. --- # 12. Accessibility checklist * All form fields have accessible labels (use `label` with `htmlFor`). * Provide keyboard navigation for stepper and drag/reorder (if implemented). * Ensure color contrast meets WCAG AA for text. * Use semantic elements (`
`, `
`, `
`, `
    `) in templates. * Add `aria-live` regions for autosave status and export progress. --- # 13. Testing & CI suggestions * **Unit tests**: Vitest/Jest for utils (`escapeHtml`, `buildPrintableHtml`). * **Visual regression**: Percy / Chromatic for templates and export output. * **E2E**: Cypress or Playwright for full editor flows (create CV → preview → export). * **CI**: run tests + lint; optionally run Puppeteer to generate sample PDFs in a job (be mindful of resource usage). Example unit test for `escapeHtml`: ```js import { escapeHtml } from '../utils/escapeHtml' test('escapes special chars', () => { expect(escapeHtml(`Tom & Jerry