Paperkit
Your PDF never leaves the tab it's opened in.
34
PDF tools
6
tool categories
0
files uploaded to a server
1
hook powering every tool
"free" PDF tools aren't free
most sites trade the missing price tag for your document — it gets uploaded to a server you can't see, just to merge two pages.
sensitive files, public servers
a contract, an ID, a bank statement — the moment it touches someone else's backend, you're trusting infrastructure you have no visibility into.
watermarks and sign-up walls
the tools that don't upload your file often gate the output behind an account, a subscription, or a stamped-on logo.
no server in the data path, by architecture
every tool — merge, compress, OCR, encrypt — runs client-side via WebAssembly and browser APIs. there's no upload step because there's no endpoint to upload to.
one hook powers all 34 tools
usePdfTool() owns file state, the idle → processing → done → error lifecycle, and progress. a new tool is "render the shell, wire one processing function" — not a rewrite.
processing logic knows nothing about React
lib/pdf, lib/convert, and lib/ai are pure File[] → Blob functions with zero UI dependency, so they're trivially testable and reusable outside the hook.
a single tool registry drives everything
config/tools.ts lists slug, name, description, icon, and category once — the homepage grid, search, and sitemap all read from that one file.
AI is the one deliberate exception
Chat, Summarize, and Compare send extracted text — never the source file — to Gemini through a serverless proxy that keeps the API key off the client.
installable, works offline
ships as a PWA. install once, and every tool except the AI ones keeps working with no connection at all.
- frontend
- Next.js 15 (App Router), TypeScript, Tailwind CSS v4
- animation
- Framer Motion
- pdf core
- pdf-lib (create/modify) + pdfjs-dist (render/parse)
- ocr
- Tesseract.js — in-browser, WebAssembly
- office formats
- Mammoth, docx, SheetJS (xlsx)
- generation
- jsPDF + html2canvas
- ai
- Google Gemini via a serverless proxy route
- deployment
- Vercel — static-first, edge-served
why client-side over a backend that just deletes files after?
"we delete it after" is a policy, not a guarantee — it still requires trusting a server you can't inspect. running entirely in the browser removes the trust requirement instead of asking for it.
why one usePdfTool hook instead of per-tool state?
34 tools sharing the same upload → process → download shape means the lifecycle only needs to be built, and debugged, once.
why send extracted text to Gemini instead of the PDF itself?
the AI features are genuinely useful, but they don't need the source file — only its text. sending the minimum necessary data keeps the "never leaves your device" promise honest for the other 31 tools.
why pdf-lib and pdfjs-dist instead of a single library?
pdf-lib is built for creating and modifying documents; pdfjs-dist is built for rendering and parsing them. using one for each job beats forcing one library to do both awkwardly.