Timelinx

TimelineEditor

The full timeline editor component - props, layout structure, keyboard shortcuts, and customization

TimelineEditor is the primary drop-in component. It assembles every panel into a complete NLE-like editing surface: top nav, toolbar, ruler, track list, track rows, transport controls, sidebar, asset bin, and media preview.

Live Example

Loading...

Basic Usage

'use client'; // Next.js only

import { TimelineEngine } from '@timelinx/react';
import { TimelineEditor } from '@timelinx/ui';
import {
  createTimelineState,
  createTimeline,
  frameRate,
  toFrame,
} from '@timelinx/core';

// Create the engine once, outside the component
const engine = new TimelineEngine({
  initialState: createTimelineState({
    timeline: createTimeline({
      id: 'tl-1',
      name: 'My Edit',
      fps: frameRate(30),
      duration: toFrame(9000),
    }),
  }),
});

export default function EditorPage() {
  return (
    <div style={{ height: '100vh' }}>
      <TimelineEditor engine={engine} />
    </div>
  );
}

TimelineEditor fills its container. Always give the container an explicit height.

Props

Prop

Type

Key Props

PropTypeDefaultNotes
engineTimelineEnginerequiredThe engine instance. Create outside the component.
defaultToolstring'selection'Active tool on mount.
ppfnumber6Initial pixels per frame (zoom level).
showTopNavbooleantrueShow the top navigation bar with project name.
showSidebarbooleantrueShow the right-side panel.
showAssetBinbooleantrueShow the asset browser in the sidebar.
showCompositorPreviewbooleantrueShow the video preview in the sidebar.
onSave(state: TimelineState) => voidnoneCalled when the user presses ⌘S.
onExport() => voidnoneCalled when the user clicks Export.
classNamestringnoneAdditional CSS class on the root element.

Layout Structure

┌─────────────────────────────────────────────────────────────┐
│ TopNav   [project name]                [save] [export]       │
├───────────────┬─────────────────────────────────────────────┤
│ TimelineToolbar (tools, undo/redo, zoom)                     │
├───────────────┬─────────────────────────────────────────────┤
│               │ TimelineRuler (timecode, ticks)              │
│               ├─────────────────────────────────────────────┤
│  Sidebar      │ TrackList  │  Track rows                     │
│  (preview +   │  (headers) │  (clips, playhead)              │
│   asset bin)  │            │                                 │
│               ├─────────────────────────────────────────────┤
│               │ TransportControls (play, stop, timecode)     │
└───────────────┴─────────────────────────────────────────────┘

Self-Wrapping Provider

TimelineEditor automatically wraps itself in TimelineProvider if no provider exists in the component tree above it. You do not need to add TimelineProvider manually when using TimelineEditor.

When using decomposed components, add TimelineProvider explicitly. See Custom Layout.

Keyboard Shortcuts

ShortcutAction
SpacePlay / Pause
JPlay backward (hold to increase speed)
KPause
LPlay forward
/ Step 1 frame
Shift+← / Shift+→Step 10 frames
HomeGo to first frame
EndGo to last frame
ISet in-point
OSet out-point
VSelect tool
CRazor / Slice tool
TRipple Trim tool
RRoll Trim tool
SSlip tool
⌘Z / Ctrl+ZUndo
⌘⇧Z / Ctrl+YRedo
Delete / BackspaceDelete selected clips
⌘A / Ctrl+ASelect all clips
⌘S / Ctrl+SSave (calls onSave prop)
?Toggle keyboard shortcut overlay

Controlling the Engine Externally

The engine is fully accessible outside the component. Call methods directly:

// From anywhere with access to the engine reference
engine.dispatch(transaction);
engine.undo();
engine.activateTool('razor');
engine.setPlayheadFrame(toFrame(450));

Saving State

The editor does not auto-save. Call onSave and write the state to your storage:

import { serializeProject } from '@timelinx/core';

<TimelineEditor
  engine={engine}
  onSave={(state) => {
    const json = JSON.stringify(serializeProject(state));
    fetch('/api/project', { method: 'POST', body: json });
  }}
/>

State comes from the engine

The onSave callback receives the current state from engine.getSnapshot().state. If you dispatch further changes after this call, the save will be stale. Always call serializeProject inside the onSave callback, not outside it.

On this page