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 & Providersapp/layout.tsx, app/providers.tsx, service worker, chunk recovery.
  • Editor routesapp/(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 by useExportEngine.
  • 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 register
providers.tsx — Redux store, export context, theming
(pages)/projects — editor surface (panels, timeline, dialogs)
(pages)/docs — documentation site
components/ — shared UI (header, panels, players)
hooks/ — export hooks, worker orchestration, UX helpers
store/ — slices, middleware, selectors
utils/ — storage manager, export utils, performance helpers
contexts/ — export provider and global contexts
api/ — 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, and audioEffects 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.

  1. circularReferenceDetector: strips accidental circular references before persistence.
  2. stateUpdateQueueMiddleware: batches high-frequency actions (drag, resize) into coherent updates.
  3. durationMiddleware: debounces timeline duration recalculations across tracks.
  4. validationMiddleware: enforces invariants (time bounds, row indices, transition pairing).
  5. 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 (see public/_headers and middleware.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 and app/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 useExportEngineexposes 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 inspects Origin/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; the ThemeProvider syncs dark/light modes across routes.
  • Analytics: @vercel/analytics is injected in production via layout.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

  1. 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.
  2. Server integrations: extend app/api with new route handlers and register security options in app/lib/apiSecurity.ts. Update middleware.ts constants if additional origins or headers are required.
  3. Alternate storage: swap storageManager implementations to back projects with a remote database while keeping the Redux API unchanged.
  4. Custom exporters: add engines under app/utils/export/exportEngines and register them through useExportEngine or ExportProvider to appear in the Export dialog.
  5. 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.