An open-source React video timeline editor for building powerful video editing experiences into your own applications.
Build professional video editing into React and Next.js products: multi-track timeline, clips, playback, and a JSON project document you own.
This repository is not Ektie Creative. It is the embeddable editor. Ektie Creative is a separate commercial product for teams that want a complete AI-powered creative production platform rather than a component to ship themselves.
| Open-source editor | Ektie Creative | |
|---|---|---|
| What it is | React NLE you embed | End-to-end creative production SaaS |
| Who it is for | Developers building a product | Teams that need films, stills, and campaign creative shipped |
| You get | Timeline, tracks, clips, preview, JSON | Studio pipeline, AI generation, brand context, managed infra |
Live demo: react-video-timeline-editor-nextjs.vercel.app
Docs: API · Data model · Architecture · Next.js · Customization
Local demo: npm run demo → http://localhost:3005
@ektie/react-video-timeline-editor is a multi-track nonlinear editor (NLE) implemented in React. You mount it in your app, pass a project document, and receive edits through onChange. There is no backend, no auth, and no required environment variables.
Who it is for. Product engineers who need an editing surface inside a SaaS, an internal tool, or an AI video wrapper — without writing a timeline from scratch.
What problem it solves. Tracks, clip geometry, trim/split/move, undo, captions, and a browser preview are a large, specialized UI. This package is that infrastructure. You keep persistence, media hosting, encoding, and product chrome.
Your React / Next.js application
↓
@ektie/react-video-timeline-editor
↓
Timeline · tracks · clips · assets · playback
The package is licensed under the GNU Affero General Public License v3.0. You may use it commercially; if you distribute the editor or offer a modified version as a network service, AGPL-3.0 requires you to provide the corresponding source.
Two different products.
- The open-source editor is editing infrastructure. You embed it, host it, and build your own workflows around the JSON document.
- Ektie Creative is a complete creative production platform: briefs → concepts → stills and films through Creative Studio, brand context, a media library, and (when you connect them) Demand and Sales so creative is judged by what converts — not only what looks finished.
Neither is a trial of the other. The editor is not a limited demo of Ektie Creative. Ektie Creative is not “the editor plus a license key.”
| Capability | Open-source editor | Ektie Creative |
|---|---|---|
| React multi-track timeline | Yes | Yes (product editing surface) |
| Video, stills, voice, music, captions | Yes | Yes |
| Trim, split, move, undo/redo, playback | Yes | Yes |
| Embed in your application | Yes | No |
| AGPL-3.0, self-host the editor | Yes | No |
| JSON project you persist anywhere | Yes | Hosted projects and library |
| Bundled encoder / cloud render | No — you plug onExport |
Yes (managed production) |
| Film production pipeline (concept → stages → ship) | No | Yes |
| Story / storyboard-style production | No | Yes |
| AI video and UGC-style films | No | Yes |
| AI stills and campaign assets | No | Yes |
| Brand-aware generation | No | Yes |
| Competitor ad remix | No | Yes |
| Shared media library for GTM | No | Yes |
| Managed SaaS infrastructure | No | Yes |
Use this package when you want a video editing foundation with full control: your UI, your storage, your encoder, your AI.
Use Ektie Creative when you do not want to build the rest of the production system: concepting, story development, storyboards, AI generation, brand context, staged production, a media library, and the loop into campaigns.
Use the open-source editor when you want to build your own video editing product. Use Ektie Creative when you want the complete AI-powered creative production system.
If you want a complete AI-powered creative production platform rather than an embeddable editor, see Ektie Creative.
Create videos. Generate assets. Build stories. Automate production.
Implemented in this repository:
- Multi-track timeline: video, stills, voice, music, captions, zoom, spotlight
- Browser preview with aspect presets (
9:16,4:5,1:1,16:9) - Clip move, trim, split, nudge, ripple delete, edge snap
- Drag on the board; magnetic snap to clip edges and the playhead
- Selection, undo/redo, timeline zoom / fit
- Clip speed (UI presets
1/1.5/2/3) - Dissolve between abutted clips (
transition_out) - Picture-in-picture and split-screen on picture clips
- Source crop, zoom punch-in/out, spotlight
- Canvas backdrop, intro, and outro plates
- Captions with host-supplied font families (preview loads Google Fonts CSS)
- Host asset library + local file import via
onImportFiles - JSON serialize / hydrate (
onChangeemits the full document) - Header slots for host Save / Export chrome
- CSS variables for accent and surface colors
Not in this package: encoding/export (no ffmpeg, no wasm encoder), networking, auth, AI generation, collaboration, or a plugin/command API.
Live: https://react-video-timeline-editor-nextjs.vercel.app
demo/nextjs is a host application. It is not the editor. It shows how a Next.js App Router app imports @ektie/react-video-timeline-editor.
git clone <this-repository>
cd react-video-timeline-editor
npm install
npm run demoOpen http://localhost:3005. Root npm run demo runs next dev --port 3005 in demo/nextjs.
The demo covers:
- Loading a sample project (local
/samples/media — not from Ektie storage) - Blank project via
createEmptyProject - Asset library items (
assets) and disk import (onImportFiles) - Editing on the board (select, split, play, zoom)
onChangeevent log- Save/load through
localStorageand download/upload of project JSON - A
slots.headerEnd“Save JSON” control
Reference files: demo/nextjs/components/EditorDemo.js, demo/nextjs/components/EditorHost.js.
There is no .env. The editor does not read environment variables.
Peer dependencies: React 18+ and react-dom.
npm install @ektie/react-video-timeline-editorUntil the package is published to npm, depend on this repo’s workspace package (packages/editor) or a file: / git URL.
"use client";
import { useState } from "react";
import {
VideoTimelineEditor,
createEmptyProject,
} from "@ektie/react-video-timeline-editor";
import "@ektie/react-video-timeline-editor/style.css";
export default function Editor() {
const [project, setProject] = useState(() =>
createEmptyProject({ id: "cut-1", name: "My cut" })
);
return (
<VideoTimelineEditor
project={project}
onChange={setProject}
/>
);
}The editor uses the DOM. Do not import it from a Server Component. Next.js 15 also forbids ssr: false on next/dynamic in Server Components — put the dynamic import in a client module (see demo/nextjs/components/EditorHost.js).
"use client";
import dynamic from "next/dynamic";
const EditorDemo = dynamic(() => import("./Editor"), { ssr: false });
export default function EditorHost() {
return <EditorDemo />;
}Full walkthrough: docs/nextjs.md.
Host app
└── VideoTimelineEditor // public component
├── project / onChange // JSON document in, JSON document out
├── assets / onImportFiles // your library and uploads
├── fonts / slots / onExport
├── useTimelineEngine // undo, selection, persist revision
├── TimelineClipModel // pure reducers (testable, no React)
└── TimelinePlayer // browser preview only
- Project state lives in the host (
useState, your store, your API). The package does not write to disk or the network. - Tracks are typed rows (
video,still,audio,subtitle,music,zoom,spotlight). Picture rows (video + still) are a z-stack;video-0is the occupying spine. - Clips are timed items on a row. Media clips use
position+ sourcein/out+speed. Captions, zoom, and spotlight use absolutein/out. - Assets are host objects. The picker never fetches your CMS; you pass
assetsand resolve uploads inonImportFiles. - Playback is an in-browser composite of
<video>/<audio>/ images. It is a preview, not a renderer. - Export is a host concern.
onExportandslots.headerEndare hooks. No encoder ships in the package.
Headless helpers are exported for hosts that want to mutate documents without mounting the UI: normalizeTracks, serializeTracks, moveClips, trimClip, splitAt, insertClip, createEmptyProject, useTimelineEngine, TimelinePlayer.
onChange receives a serialized document. This is what you persist.
{
"id": "cut-1",
"name": "Product walkthrough",
"aspect_ratio": "9:16",
"resolution": "1080x1920",
"canvas": {
"backdrop": { "enabled": false, "mode": "color", "color": "#000000" }
},
"tracks": [
{
"id": "video-0",
"type": "video",
"items": [
{
"position": 0,
"in": 0,
"out": 4000,
"speed": 1,
"label": "Hook",
"video_url": "https://cdn.example/clip.mp4",
"keyframe_url": "https://cdn.example/poster.png",
"transition_out": { "type": "dissolve", "duration_ms": 250 }
}
]
},
{
"id": "audio-0",
"type": "audio",
"items": [
{
"position": 0,
"in": 0,
"out": 4000,
"speed": 1,
"label": "VO",
"audio_url": "https://cdn.example/voice.wav"
}
]
},
{
"id": "subtitle-0",
"type": "subtitle",
"items": [
{ "in": 400, "out": 2800, "text": "Ship the cut from JSON." }
]
}
],
"subtitles": [{ "in": 400, "out": 2800, "text": "Ship the cut from JSON." }],
"total_duration_ms": 4000
}createEmptyProject({ id, name, aspectRatio, resolution }) returns a valid empty board (required rows, default 9:16 / 1080x1920).
Media clip fields: position (timeline start, ms), in / out (source trim, ms), speed (wall duration is (out - in) / speed), video_url / audio_url / keyframe_url. Optional: transition_out, pip, split, source_crop. Unknown keys round-trip.
Cue clips (subtitle, zoom, spotlight): absolute in / out. Zoom/spotlight also use rect: { x, y, w, h } in 0–1 of the picture (not letterbox bars).
Assets you pass into the picker:
{
key: "clip-1",
group: "Library",
title: "Teal clip",
subtitle: "4s video",
thumbnail: "/samples/still.png",
types: ["video"],
draft: {
label: "Teal clip",
durationMs: 4000,
video_url: "/samples/clip.mp4",
keyframe_url: "/samples/still.png",
},
}onImportFiles({ files, trackType }) should return a clip draft (label, durationMs, and video_url / audio_url / keyframe_url). Blob URLs are fine; serve remote media with CORS if you need filmstrips/waveforms.
Details: docs/data-model.md.
| Prop | Role |
|---|---|
project |
Serialized timeline document |
onChange |
Called with the next full document after a committed edit |
assets |
Library items for the media picker |
onImportFiles |
{ files, trackType } → clip draft |
fonts |
Caption font family names (default list in the package) |
aspectRatio / resolution |
Preview frame (falls back to project) |
saveState / savedAt / onRetrySave |
Host save status chrome |
onExport |
Optional export action — no encoder inside the package |
slots.headerStart / slots.headerEnd |
Host controls in the toolbar |
className |
Wrapper class |
slots.toolbarExtra, slots.contextMenuExtra, and slots.emptyState appear in the TypeScript definitions but are not wired in the current UI. Do not rely on them yet.
v1 extension points (wrap the package; do not fork internals for product features):
- Asset library —
assets+onImportFiles - Fonts —
fonts(string families); preview injects Google Fonts CSS - Persistence —
onChange; store JSON wherever you like - Export —
onExportorslots.headerEnd - Chrome —
slots.headerStart,slots.headerEnd - Theming — CSS variables on
:root(--vte-accent,--card,--muted,--border,--foreground,--background, …) - Passthrough fields on clips (ids, roles, metadata survive serialize/hydrate)
Not available: a plugin system, custom track types, custom clip renderers, or a public command bus. Keyboard shortcuts (Space, S split, ⌘Z / Ctrl+Z, arrows, +/- zoom, Delete) are built into the editor, not a host API.
Embed the editor when you are building:
- A video creation or AI-video SaaS (you supply generation + encode)
- Social / UGC / marketing video tools
- Education or course-authoring products
- Internal production consoles
- Product-demo or digital-signage builders
- Collaboration products (you supply presence and storage)
The editor is the timeline. Your application is the product.
Ektie Creative is one product built around this class of editing technology. You are not required to use it.
Open-source video editor
↓
Reusable editing infrastructure
↓
You build your own product
or
Open-source video editor
↓
Ektie Creative
↓
AI-powered creative production platform
packages/editor @ektie/react-video-timeline-editor (the library)
demo/nextjs Next.js App Router host (reference integration)
docs/ API, data model, architecture, Next.js, security audit
examples/ Minimal host snippet
Requirements: Node.js 18.18+ (Next.js 15 demo), npm 7+ (workspaces).
npm install # hoist workspaces
npm test # packages/editor node:test suite
npm run demo # Next.js demo at http://localhost:3005
npm run build # Vite build of the editor package
npm run pack:editor # npm tarball of @ektie/react-video-timeline-editor
npm run security-scanThere is no repo-level lint or tsc script today. Types live in packages/editor/src/index.d.ts.
Editor code belongs in packages/editor. demo/nextjs should only consume the public API.
External contributions are welcome.
- Fork the repository and create a branch from the default branch.
- Keep changes in
packages/editorunless you are improving the demo or docs. - Do not add credentials,
.envfiles, customer media, or private host URLs. - Run
npm test. Runnpm run security-scanif you copied code or comments from another tree. - Open a pull request with a short description of why the change exists.
Bug reports and feature requests: open an issue with reproduction steps (editor version, browser, a synthetic project JSON — no customer media).
Coding expectations: match nearby style; prefer pure reducers in TimelineClipModel.js for edit math; keep the demo a consumer, not a second copy of the NLE.
See CONTRIBUTING.md.
Available now — embeddable NLE, JSON document, Next.js reference demo, AGPL-3.0.
Planned (this repository)
- npm publish of
@ektie/react-video-timeline-editor - Completer TypeScript types for tracks/clips
- Wire or remove unused
slots.*declarations - Host-side export examples (your encoder, not a bundled encoder runtime)
Not planned here: AI generation, storyboards, managed render farms, or Ektie Creative’s production pipeline. Those belong in host applications (including Ektie Creative).
The editor needs zero environment variables. Do not copy secrets into this repo.
Report vulnerabilities privately (GitHub security advisory once the repo is public, or a private message to the maintainers). Do not file a public issue with exploit details.
Audit notes for maintainers: docs/SECURITY-AUDIT.md.
Licensed under the GNU Affero General Public License v3.0. See LICENSE and NOTICE.
The package depends on lucide-react (ISC). Caption preview may load stylesheet URLs from Google Fonts at runtime; those fonts are not vendored in this repository.
No extra field-of-use restrictions are added beyond AGPL-3.0.