Live site: https://viveknaskar.github.io/pdf-kit/
Free, open-source PDF tools that run entirely in your browser. No watermarks, no sign-up, no server uploads, your files never leave your device.
Built with vanilla JavaScript, pdf-lib, PDF.js, and Vite.
| Merge PDFs | Split PDF | Compress PDF |
|---|---|---|
![]() |
![]() |
![]() |
| PDF to JPG | Images to PDF | HTML to PDF |
|---|---|---|
![]() |
![]() |
![]() |
| Organize Pages | Add Text / Sign | Page Numbers |
|---|---|---|
![]() |
![]() |
![]() |
| Add Watermark | Encrypt PDF | Extract Text |
|---|---|---|
![]() |
![]() |
![]() |
- 100% client-side: all processing happens in the browser using WebAssembly and Canvas APIs
- No file uploads: files are read and written locally; nothing is sent to any server
- Works offline: once loaded, the app functions without an internet connection
- No watermarks, no sign-up, no limits
- Drag & drop support across all tools
When you upload a file, it never leaves your device. Here is exactly what happens:
- The browser reads the file into memory using the
FileReader/ArrayBufferAPI. The file data lives in your browser's RAM, not on any server. - pdf-lib and PDF.js process it locally inside your browser tab using JavaScript and WebAssembly.
- The result is generated in memory as a new
Blobobject, still entirely in RAM. - You download it via a temporary local URL (
URL.createObjectURL) that the browser creates, triggers the download, then immediately revokes. - When you close the tab, everything is gone. No trace left anywhere.
No data is ever sent over the network. There is no backend, no database, and no cloud storage involved. You can turn off your WiFi after the page loads and every tool will still work.
| Tool | Description |
|---|---|
| Merge PDFs | Combine multiple PDF files into one. Drag to reorder before merging. |
| Split PDF | Extract selected pages or split every page into a separate PDF. Visual page picker included. |
| Compress PDF | Reduce file size. Most effective on PDFs with embedded images. |
| Tool | Description |
|---|---|
| PDF to JPG | Render each PDF page as a JPG image. Choose Standard (72 DPI), High (150 DPI), or Maximum (216 DPI) quality. |
| Images to PDF | Combine JPG, PNG, or WebP images into a single PDF document. |
| HTML to PDF | Paste raw HTML and convert it to a downloadable PDF. |
| Tool | Description |
|---|---|
| Organize Pages | Drag to reorder pages, rotate 90° CW/CCW, or delete pages with visual thumbnails. |
| Add Text / Sign | Click to place text annotations or freehand-draw a signature on any page. Supports color and font size controls, undo, and multi-page navigation. |
| Page Numbers | Add page numbers with configurable position (top/bottom, left/center/right), format (plain, dash, "Page N", "N of M"), and starting number. |
| Add Watermark | Stamp a diagonal text watermark on every page. Configurable text and opacity (light 10%, medium 20%, heavy 35%). |
| Encrypt PDF | Password-protect a PDF using pdf-lib's built-in encryption. |
| Extract Text | Extract all text content from a PDF and copy it to the clipboard. |
| Library | Role |
|---|---|
| pdf-lib | Create and modify PDFs (merge, split, annotate, encrypt, watermark, page numbers) |
| pdfjs-dist | Render PDF pages to canvas (previews, PDF to JPG, text extraction) |
| Vite | Dev server, ES module bundler, and production build tool |
No frontend framework — plain HTML, CSS, and ES modules.
- Node.js v18 or later (v24 LTS recommended)
- npm (bundled with Node.js)
# Install dependencies
npm install
# Start the dev server
npm run devOpens automatically at http://localhost:3000.
npm run buildOutput goes to dist/. This is a fully static folder — deploy it anywhere.
# Preview the production build locally
npm run preview# Unit tests (Utils.js)
npm test
# Unit tests in watch mode
npm run test:watch
# E2E tests (requires no other process on port 5173)
npm run test:e2eUnit tests use Vitest with happy-dom. E2E tests use Vitest + Puppeteer and spin up a Vite dev server automatically.
pdf-kit/
├── public/
│ ├── favicon.svg
│ ├── og-image.png
│ └── robots.txt
├── src/
│ ├── styles/
│ │ ├── base.css # CSS reset, variables, typography
│ │ ├── layout.css # Header, footer, hero, tool grid
│ │ ├── components.css # Cards, buttons, dropzones, progress bars
│ │ └── tools.css # Canvas editor, page previews, toolbars
│ ├── core/
│ │ ├── App.js # App shell HTML, routing, navigation
│ │ ├── DropZone.js # Drag-and-drop file handling
│ │ └── Utils.js # Shared utility functions
│ ├── tools/
│ │ ├── MergePdf.js
│ │ ├── SplitPdf.js
│ │ ├── CompressPdf.js
│ │ ├── PdfToJpg.js
│ │ ├── ImagesToPdf.js
│ │ ├── HtmlToPdf.js
│ │ ├── OrganizePages.js
│ │ ├── AddTextSign.js
│ │ ├── PageNumbers.js
│ │ ├── AddWatermark.js
│ │ ├── EncryptPdf.js
│ │ └── ExtractText.js
│ └── main.js # Entry point — imports and initializes all tools
├── tests/
│ ├── unit/
│ │ └── utils.test.js # Unit tests for Utils.js
│ └── e2e/
│ ├── setup.js # Vite dev server setup/teardown
│ └── app.test.js # Puppeteer E2E tests
├── index.html
├── package.json
├── vite.config.js
├── vitest.config.js # Unit test config
├── vitest.e2e.config.js # E2E test config
└── .gitignore
- Create
src/tools/YourTool.jsand export aninit()function that binds event listeners - Add the tool view HTML inside
toolViewsHTML()insrc/core/App.js - Add a tool card to the appropriate section grid in the home view (also in
App.js) - Import and call
init()insrc/main.js













