Logoreact-timeline-editor
Core Concepts

Data Interfaces

A deep-dive into TimelineRow, TimelineAction, and TimelineEffect — the three core types that power the timeline editor.

The React Timeline Editor is built around three core data types exported from @keplar-404/react-timeline-editor.

TimelineRow

A Row (or Track) is the vertical lane in your timeline. Each row contains an ordered list of action blocks.

export interface TimelineRow {
  /** Row unique ID */
  id: string;
  /** List of actions contained in this row */
  actions: TimelineAction[];
  /** Custom CSS class for styling the row */
  classNames?: string[];
  /** Whether the row is in a "selected" state */
  selected?: boolean;
  /** Flexible row height (overrides rowHeight prop) */
  flexy?: boolean;
}

TimelineAction

An Action is a time-bounded block sitting inside a TimelineRow. It is the fundamental unit of the timeline.

export interface TimelineAction {
  /** Action unique ID */
  id: string;
  /** Start time of the action in seconds */
  start: number;
  /** End time of the action in seconds */
  end: number;
  /** ID of the effect that defines this action's behavior */
  effectId: string;
  /** If false, the action cannot be dragged horizontally. Defaults to true. */
  movable?: boolean;
  /** If false, the action cannot be resized. Defaults to true. */
  flexible?: boolean;
  /** Optional minimum start position (clamps dragging). */
  minStart?: number;
  /** Optional maximum end position (clamps dragging). */
  maxEnd?: number;
  /** Inline CSS styles for the action block */
  style?: React.CSSProperties;
  /** Additional CSS class name for the action block */
  className?: string;
  /** Custom data payload — attach any extra metadata here */
  data?: any;
}

You can extend TimelineAction with your own TypeScript interface to add typed custom data:

export interface MyAudioAction extends TimelineAction {
  data: {
    src: string;
    volume: number;
  };
}

TimelineEffect

An Effect is the mapping between an action's effectId and the logic that runs during playback. Effects live in a flat Record<string, TimelineEffect> and are passed to the <Timeline /> via the effects prop.

export interface TimelineEffect {
  /** Effect ID (must match the effectId in your TimelineAction) */
  id: string;
  /** Human-readable name */
  name: string;
  /** Engine source callbacks — define what happens during playback */
  source?: TimeLineEffectSource;
}

TimeLineEffectSource Callbacks

The source property contains lifecycle callbacks that the engine fires during playback:

export interface TimeLineEffectSource {
  /** Called when playback starts while cursor is already inside this action */
  start?: (params: EffectSourceParams) => void;
  /** Called when the playhead enters this action's time range */
  enter?: (params: EffectSourceParams) => void;
  /** Called every animation frame while inside this action's range */
  update?: (params: EffectSourceParams) => void;
  /** Called when the playhead leaves this action's time range */
  leave?: (params: EffectSourceParams) => void;
  /** Called when playback is paused while inside this action */
  stop?: (params: EffectSourceParams) => void;
}

Engine Callback Lifecycle

CallbackFires when...Typical use
startPlayback begins while cursor is already within the action rangeResume audio at correct offset
enterPlayhead crosses into the action from outsideStart sound / show animation
updateEvery tick while inside the actionAdvance animation frame
leavePlayhead crosses out of the actionStop audio / hide animation
stopPlayback is paused inside the actionPause audio

A Full Example Setup

import { TimelineRow, TimelineEffect } from "@keplar-404/react-timeline-editor";
import { TimelineEngine } from "@keplar-404/timeline-engine";

// ── Effect definitions ──────────────────────────────────────────────────────
const mockEffect: Record<string, TimelineEffect> = {
  audio: {
    id: "audio",
    name: "Play Audio",
    source: {
      enter: ({ action, time }) => {
        console.log(`Playing audio for action ${action.id} at t=${time}`);
      },
      leave: ({ action }) => {
        console.log(`Stopping audio for action ${action.id}`);
      },
    },
  },
  video: {
    id: "video",
    name: "Play Video",
  },
};

// ── Timeline data ──────────────────────────────────────────────────────────
const editorData: TimelineRow[] = [
  {
    id: "audio-track",
    actions: [
      {
        id: "audio-clip-1",
        start: 0,
        end: 10,
        effectId: "audio",
        data: { src: "/audio/bg.mp3", name: "Background Music" },
      },
    ],
  },
  {
    id: "video-track",
    actions: [
      {
        id: "video-clip-1",
        start: 2,
        end: 8,
        effectId: "video",
        movable: true,
        flexible: true,
        minStart: 0,
        maxEnd: 20,
      },
    ],
  },
];

On this page