A local-first PDF presentation tool.
Upload a PDF → get a dual-window presenter with a fullscreen view and a controller — all running 100% in your browser.
PresentPDF turns any PDF into a full-screen, keyboard-navigable presentation — with a separate control panel for the presenter and a present view for the audience. No file uploads to a server; everything stays in the browser using IndexedDB.
| Feature | Description |
|---|---|
| Drag & Drop Upload | Land on the homepage, drop a PDF, and you're in |
| Dual-Window Mode | Open /present on the projector and /control on your laptop — they stay in sync |
| Smart Background | The present view samples the PDF's edge colors and adapts the page background to match |
| Keyboard Navigation | Arrow keys navigate slides from either window |
| Local-Only Storage | PDFs are stored in the browser via localforage / IndexedDB — nothing leaves your machine |
| High-Fidelity Rendering | 2× supersampled canvas rendering via react-pdf for crisp text and vectors |
| Pre-Rendering | Pages around the current slide are rendered ahead of time for instant transitions |
┌────────────────────────────────────────────────────┐
│ Browser (Same Origin) │
│ │
│ ┌──────────┐ BroadcastChannel ┌────────────┐ │
│ │ /control │ ◄──────────────────► │ /present │ │
│ │ (laptop) │ slide sync │ (projector)│ │
│ └────┬─────┘ └─────┬──────┘ │
│ │ │ │
│ └──────────┐ ┌──────────────────┘ │
│ ▼ ▼ │
│ ┌──────────────┐ │
│ │ IndexedDB │ │
│ │ (localforage)│ │
│ └──────────────┘ │
└────────────────────────────────────────────────────┘
| Route | Purpose |
|---|---|
/ |
Landing page — drag-and-drop PDF upload |
/control?id=<uuid> |
Presenter control panel with live preview, slide counter, and navigation |
/present?id=<uuid> |
Fullscreen audience view with adaptive background |
- Node.js ≥ 20
- npm ≥ 9
# Clone the repo
git clone https://github.com/iamcoder18/PresentPDF
cd PresentPDF
# Install dependencies
npm install
# Start the dev server
npm run devOpen http://localhost:3000 and drop a PDF.
npm run build
npm start# Build and run with Docker Compose
docker compose up --build
# Or build the image directly
docker build -t presentpdf .
docker run -p 3000:3000 presentpdfThe app will be available at http://localhost:3000.
The included docker-compose.yml provides:
- Port mapping (
3000:3000) - Health check (auto-restarts if unhealthy)
restart: unless-stoppedpolicy
# Start in background
docker compose up -d
# View logs
docker compose logs -f
# Stop
docker compose downPresentPDF/
├── src/
│ ├── app/
│ │ ├── page.tsx # Landing page (upload)
│ │ ├── layout.tsx # Root layout (Space Mono font)
│ │ ├── globals.css # Tailwind + brutalist theme tokens
│ │ ├── control/page.tsx # Presenter control panel
│ │ └── present/page.tsx # Fullscreen presentation view
│ ├── components/
│ │ ├── pdf/PDFViewer.tsx # Core PDF renderer (react-pdf)
│ │ └── ui/button.tsx # Shadcn button component
│ ├── hooks/
│ │ └── useSyncState.ts # BroadcastChannel sync hook
│ └── lib/
│ ├── storage.ts # localforage PDF persistence
│ └── utils.ts # Utility helpers
├── Dockerfile # Multi-stage production build
├── docker-compose.yml # Compose orchestration
├── .dockerignore # Build context exclusions
├── next.config.ts # Next.js config (standalone output)
├── package.json
└── tsconfig.json
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19 |
| PDF Engine | react-pdf + pdf.js |
| Styling | Tailwind CSS 4 + Shadcn UI |
| Storage | localforage (IndexedDB) |
| Cross-Tab Sync | BroadcastChannel API |
| Font | Space Mono (Google Fonts) |
MIT