Quick Start
Build a working timeline editor in under 5 minutes
What You're Building
A fully functional timeline editor with video tracks, clip placement, zoom, drag-to-move, undo/redo, and keyboard shortcuts - running entirely in the browser.
Core
The headless engine. Dispatch model, invariant validation, and undo history.
React
React bindings - TimelineEngine, hooks, and providers.
UI
Drop-in components - tracks, clips, ruler, panels, and a complete editor surface.
Media Web
Browser-native adapters - WebCodecs decoding, WebAudio waveforms, WebGL compositing, and export.
Prerequisites
- Node.js >= 22
- React >= 18 (Next.js, Vite, or any setup)
- TypeScript >= 5.0
TypeScript is not required, but strongly recommended. All packages are written in TypeScript with full type definitions.
Install
Install packages
pnpm add @timelinx/core @timelinx/react @timelinx/uiAdd CSS imports
Add the CSS to your global stylesheet. Both imports are required:
@import '@timelinx/ui/styles/presets/dark-pro';
@import '@timelinx/ui/styles/structure';Both CSS imports are required. The preset defines colors and tokens, while structure defines layout and component styles.
See Installation for Next.js, Vite, and framework-specific notes.
Create an Initial State
State is a plain object. Build it once with factory functions:
import {
createTimeline,
createTimelineState,
createTrack,
createClip,
createAsset,
dispatch,
toFrame,
toTrackId,
toAssetId,
frameRate,
} from '@timelinx/core';
// 10-minute timeline at 30fps
const timeline = createTimeline({
id: 'tl-1',
name: 'My Edit',
fps: frameRate(30),
duration: toFrame(18000),
});
const emptyState = createTimelineState({ timeline });
// A video asset must be registered before any clip can reference it
const videoAsset = createAsset({
id: 'asset-1',
name: 'interview.mp4',
mediaType: 'video',
filePath: '/media/interview.mp4',
intrinsicDuration: toFrame(9000), // 5 minutes
nativeFps: frameRate(30),
sourceTimecodeOffset: toFrame(0),
});
const videoTrack = createTrack({ id: 'v1', name: 'V1', type: 'video' });
const result = dispatch(emptyState, {
id: 'tx-setup',
label: 'Setup timeline',
timestamp: Date.now(),
operations: [
// 1. Register the asset first - clips must reference an existing asset
{ type: 'REGISTER_ASSET', asset: videoAsset },
// 2. Add the track
{ type: 'ADD_TRACK', track: videoTrack },
// 3. Place a clip (references the asset registered in step 1)
{
type: 'INSERT_CLIP',
trackId: toTrackId('v1'),
clip: createClip({
id: 'clip-1',
assetId: toAssetId('asset-1'),
trackId: toTrackId('v1'),
timelineStart: toFrame(0),
timelineEnd: toFrame(300), // 10 seconds at 30fps
mediaIn: toFrame(0),
mediaOut: toFrame(300),
}),
},
],
});
if (!result.accepted) {
throw new Error(`Setup failed: ${result.reason} - ${result.message}`);
}
export const initialState = result.nextState;The asset registry matters. If you insert a clip that references an unknown asset, dispatch() rejects the transaction with ASSET_MISSING.
Render the Editor
Create a TimelineEngine from your initial state and pass it to TimelineEditor:
'use client'; // Next.js only
import { TimelineEngine } from '@timelinx/react';
import { TimelineEditor } from '@timelinx/ui';
import { initialState } from '@/lib/timeline';
// Create the engine once, outside the component
const engine = new TimelineEngine({ initialState });
export default function EditorPage() {
return (
<div style={{ height: '100vh' }}>
<TimelineEditor engine={engine} />
</div>
);
}TimelineEditor gives you: toolbar, ruler, track list, clip drag and resize, zoom controls, transport, sidebar, and keyboard shortcuts - all wired to the engine.
Run It
pnpm devOpen the page. You should see a dark timeline editor with one video track and a clip at the start. Try dragging the clip, pressing Cmd+Z to undo, and using the zoom controls.
What's Next
Concepts
Every core term defined with its exact TypeScript type.
Dispatch Model
How transactions, operations, and invariants work.
TimelineEngine
Full engine API - methods, options, and snapshot model.
Theming
Customize colors, fonts, and layout with CSS tokens.