Timelinx

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.

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/ui

Add CSS imports

Add the CSS to your global stylesheet. Both imports are required:

globals.css
@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:

lib/timeline.ts
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:

app/editor/page.tsx
'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 dev

Open 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

FAQ

On this page