Architecture
Klippy is designed as a privacy-first, client-forward video editor. This page breaks down the system architecture, how the editor is assembled inside the Next.js App Router, and the contracts between UI, state, storage, export engines, and backend routes so you can plan integrations or modify the stack confidently.
High-level runtime
The starter ships as a full Next.js 15 application. Server components handle routing and security headers; the editor experience is a client component tree powered by Redux and a suite of Web Workers for media processing.
App router shell
- Layout & Providers —
app/layout.tsx
,app/providers.tsx
, service worker, chunk recovery. - Editor routes —
app/(pages)/projects/[id]
renders panels, timeline, preview, dialogs. - Support routes — docs, storage, admin tools, and test harnesses.
Client runtime
- Redux slices under
app/store
with memoised selectors. - Persistence via
app/utils/storageManager.ts
(IndexedDB, quotas, bundles). - Export engines in
app/utils/export/*
coordinated byuseExportEngine
. - Worker pools for rendering, audio, CV tasks (
app/components/editor/render/**/workers
).
Server endpoints
- Stock media:
/api/pexels
,/api/tenor
,/api/meme
. - AI & transcription:
/api/ai
,/api/transcription
with strict origin checks. - Diagnostics:
/api/health
, emoji datasets, admin utilities.
Directory map
The repo is organised to keep editor-critical code under app/
, long-lived libraries insidepackages/
, and technical documentation co-located in docs/
.
app/ | layout.tsx — metadata, providers, service worker registerproviders.tsx — Redux store, export context, theming(pages)/projects — editor surface (panels, timeline, dialogs)(pages)/docs — documentation sitecomponents/ — shared UI (header, panels, players)hooks/ — export hooks, worker orchestration, UX helpersstore/ — slices, middleware, selectorsutils/ — storage manager, export utils, performance helperscontexts/ — export provider and global contextsapi/ — Next.js route handlers (stock media, AI, transcription) |
packages/ | core/ , effects/ — reusable math, effect pipelines consumed in the editor |
docs/ | Markdown references and deep-dive guides surfaced inside the docs app. |
Render & provider pipeline
app/layout.tsx
declares SEO metadata, sets up the HTML shell, registers the service worker, and injects font/CDN preload hints for editor routes.Providers
(app/providers.tsx
) wraps the tree with Redux, theme, and export contexts, then conditionally preloads fonts and FFmpeg when the user navigates into `/projects/*`.ChunkLoadRecovery
listens for chunk-loading failures and retries, avoiding blank screens after redeployments.ErrorBoundary
components isolate rendering failures in both the global shell and the editor core, providing toasts/logs without crashing the entire app.ServiceWorkerRegister
enables offline caching of WASM and static assets in production, while leaving development builds worker-free to simplify debugging.
Editor route anatomy
The main editor lives under app/(pages)/projects/[id]
. Each sub-tree is responsible for a focus area:
components/editor
— timeline rows, layer panels, media library, caption tools, export dialogs, recording UI.hooks/useExportEngine.ts
— orchestrates export mode detection (FFmpeg WASM vs MediaBunny/WebCodecs), progress reporting, and worker pooling.hooks/useWorkerManager.ts
&app/utils/workerManager.ts
— shared pool for long-lived workers (rendering, audio extraction, computer vision).contexts/ExportContext.tsx
— exposes export state, queues, and cancellation functions to UI components.components/editor/providers
(e.g.RouteGatedFontPreloaders
) — gate heavy resources behind route checks so non-editor pages stay lightweight.
Supporting routes like /storage
, /text-to-speech
, and /docs/*
reuse the same provider stack, but keep their component trees segmented to avoid shipping editor-heavy bundles unnecessarily.
State model & data flow
Redux Toolkit powers the editor’s deterministic state. The store is created in app/store/index.ts
and combines feature slices with middleware that keep state mutation, validation, and history in sync.
- Project content:
projectState
stores timeline entities (media, text, zoom, transitions, caption tracks) plus export settings and recording state. - Timeline UX:
timeline
tracks playhead, zoom, snapping, and scroll offsets.selection
holds active elements and contextual UI state. - Auxiliary slices:
transcripts
,edits
,aiComponents
,ffmpeg
,preferences
,projects
, andaudioEffects
isolate specialised capabilities. - Selectors: heavy computations (visible items, content duration, composed styles) live in
app/store/selectors/*
using Reselect to avoid O(n) recomputations on drag/scroll. - Autosave: hooks in the editor trigger debounced persistence that passes the serialisable project snapshot to the storage manager (see Persistence).
Middleware pipeline
Middleware in app/store/index.ts
executes in a deliberate order to batch updates, recompute durations, and maintain undo/redo semantics without blocking the UI.
circularReferenceDetector
: strips accidental circular references before persistence.stateUpdateQueueMiddleware
: batches high-frequency actions (drag, resize) into coherent updates.durationMiddleware
: debounces timeline duration recalculations across tracks.validationMiddleware
: enforces invariants (time bounds, row indices, transition pairing).unifiedHistoryMiddleware
&combinedUndoRedoMiddleware
: synchronise project-level history with transcript/edit stacks, enabling consistent undo across slices.
Additional middlewares (e.g. quota warnings, analytics) can be appended after this core pipeline without affecting deterministic history semantics.
Persistence & storage
app/utils/storageManager.ts
wraps IndexedDB (klippy-storage
) for project snapshots and file blobs. The same utility exposes quota checks, pruning, and bundle export helpers./storage
route surfaces usage dashboards, allowing users to clear caches or delete projects when browsers approach quota limits.- Autosave, manual save, and bundle export flows pipe through the storage manager, ensuring de-duplication before writing large blobs.
- Legacy localStorage state is automatically migrated/ignored; only IndexedDB is authoritative for project data.
Worker & export architecture
Rendering and media processing rely on dedicated worker pools to keep the UI responsive:
- FFmpeg WASM: loaded via
@ffmpeg/core
, preloaded when the Export dialog opens. Multi-threading is unlocked through COOP/COEP headers (seepublic/_headers
andmiddleware.ts
). - WebCodecs & MediaBunny:
app/utils/export/exportEngines/*
spin up encoder workers for mobile-friendly exports, falling back to MediaRecorder if hardware acceleration is unavailable. - Render workers: pools in
app/components/editor/render/**/workers
handle frame composition for transparent exports and canvas previews. - Audio & TTS: workers in
app/services/AudioExtractionService.ts
andapp/services/audioEffects/*
offload resampling, waveform generation, and effects processing. - Safe code execution:
app/services/SafeCodeExecutor.ts
sandboxes user-supplied AI components using isolated workers.
The ExportProvider
coordinates engine selection, worker lifecycle, and cancellation, while useExportEngine
exposes progress and error events to UI components.
Backend boundary
Klippy is intentionally client-first; the server is limited to secure proxies and metadata APIs. All handlers live under app/api/*
and share common security middleware.
- Origin & rate limiting:
middleware.ts
inspectsOrigin/Referer
, applies CSP/Permissions-Policy headers, and throttles requests (per-IP + per-route). - Stock media:
/api/pexels
,/api/tenor
, and/api/meme
proxy upstream APIs, rewriting headers and enforcing allowlists. - AI & transcription:
/api/ai
optionally requires client-provided keys or uses a server key;/api/transcription
forwards audio to a configured Cloudflare Worker or self-hosted Whisper service. - Diagnostics:
/api/health
reports configuration status, enabling automated checks during deployment.
Because projects stay on-device, these APIs never receive user media unless you explicitly extend the system.
Cross-cutting concerns
- Theme & design tokens: Tailwind drives styling with custom palettes defined in
tailwind.config.ts
; theThemeProvider
syncs dark/light modes across routes. - Analytics:
@vercel/analytics
is injected in production vialayout.tsx
; additional tools (PostHog, Sentry) can be toggled via environment flags documented in Dependencies. - Error handling: Toasts (react-hot-toast) surface recoverable issues, while persistent logs flow to Sentry when configured.
- Feature flags:
app/lib/feature-flags
hosts environment-driven toggles so you can stage features per deployment. - Testing harness:
app/(pages)/test-*
routes expose internal testbeds for audio export, FFmpeg, MediaBunny, IndexedDB, and workers to aid QA.
Extending the architecture
- New panels or tools: create feature slices, selectors, and UI components under
app/components/editor
; wire into the main router via lazy-loaded panels to avoid bloating initial loads. - Server integrations: extend
app/api
with new route handlers and register security options inapp/lib/apiSecurity.ts
. Updatemiddleware.ts
constants if additional origins or headers are required. - Alternate storage: swap
storageManager
implementations to back projects with a remote database while keeping the Redux API unchanged. - Custom exporters: add engines under
app/utils/export/exportEngines
and register them throughuseExportEngine
orExportProvider
to appear in the Export dialog. - Branding: reuse the same provider tree but replace shell components (
Header
,Footer
,ThemeProvider
) to make Klippy feel native inside your product.
For deeper implementation details, cross-reference State management, Rendering, and other topic guides.