Timeline
A powerful, modular timeline component for React video editing applications
The RVE Timeline is our take on a React timeline for video editing apps. We wanted something that feels powerful but doesn’t lock you into a specific stack. That’s why the timeline is built to be modular and “drop-in.” You can literally copy the component into your project, hook up a few props, and it’ll run.
Under the hood we use mediabunny, for generating video thumbnails. Beyond that, we’ve tried to keep things lean so you’re not pulling in a ton of libraries just to get a timeline running. The idea is that the component gives you a good foundation (tracks, items, scrubbing, resizing, etc.), but you stay in control of the data and how it plugs into the rest of your editor.
Note
The timeline is the hub of most video editors. It’s a complex system of state and logic that drives nearly everything, which is why we’ve put so much emphasis on it. Right now, the Timeline is actively being worked on with ongoing improvements. A lot of the focus is on performance. Making sure it can handle larger projects smoothly without slowing down.
That said, there are still some limitations. Performance can depend on the browser you’re using, the size of your files, and other factors that aren’t always in our control. This is only going to get better. We’re committed to continually refining the Timeline and rolling out improvements that make it faster, smoother, and easier to work with.
Key Features
- Multi-track Timeline: Support for unlimited tracks with different media types
- Drag & Drop: Intuitive item manipulation with magnetic snapping
- Multiselect & Marquee Selection: Select multiple items with click+drag marquee
- Zoom Controls: Smooth zooming with mouse wheel and keyboard shortcuts
- Playback Controls: Play/pause with customizable playback rates
- Keyboard Shortcuts: Comprehensive hotkey support for efficient editing
- Undo/Redo: Built-in history management with external integration support
- Context Menus: Right-click actions for timeline operations
- Mobile Support: Responsive design with mobile-optimized interactions
- Theming: CSS custom properties for light/dark/custom themes
- Splitting: Advanced item splitting with visual feedback
- Guidelines: Visual alignment helpers during drag operations
Usage Example
import React from 'react';
import Timeline from '@/components/editor/components/advanced-timeline/timeline';
import { TimelineTrack } from '@/components/editor/components/advanced-timeline/types';
const VideoEditor = () => {
const [tracks, setTracks] = React.useState<TimelineTrack[]>([
{
id: 'track-1',
name: 'Video Track',
items: [
{
id: 'item-1',
trackId: 'track-1',
start: 0,
end: 10,
type: 'video',
label: 'Video Clip 1',
color: '#3b82f6'
}
]
}
]);
const [currentFrame, setCurrentFrame] = React.useState(0);
const [isPlaying, setIsPlaying] = React.useState(false);
const [selectedItems, setSelectedItems] = React.useState<string[]>([]);
return (
<Timeline
tracks={tracks}
totalDuration={30} // 30 seconds
currentFrame={currentFrame}
fps={30}
onFrameChange={setCurrentFrame}
onTracksChange={setTracks}
selectedItemIds={selectedItems}
onSelectedItemsChange={setSelectedItems}
// Playback controls
isPlaying={isPlaying}
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
showPlaybackControls={true}
// UI features
showZoomControls={true}
showTimelineGuidelines={true}
enableTrackDrag={true}
enableMagneticTrack={true}
// Event handlers
onItemMove={(itemId, newStart, newEnd, newTrackId) => {
console.log('Item moved:', { itemId, newStart, newEnd, newTrackId });
}}
onItemResize={(itemId, newStart, newEnd) => {
console.log('Item resized:', { itemId, newStart, newEnd });
}}
onDeleteItems={(itemIds) => {
console.log('Delete items:', itemIds);
}}
/>
);
};
Component Structure
Timeline/
├── components/
│ ├── timeline-header/ # Header with controls
│ ├── timeline-content.tsx # Main timeline area
│ ├── timeline-track.tsx # Individual track component
│ ├── timeline-item/ # Timeline item components
│ ├── timeline-markers.tsx # Time markers
│ ├── timeline-guidelines.tsx # Drag guidelines
│ └── timeline-track-handles.tsx # Track control handles
├── hooks/
│ ├── use-timeline-zoom.ts # Zoom functionality
│ ├── use-timeline-tracks.ts # Track management
│ ├── use-timeline-shortcuts.ts # Keyboard shortcuts
│ ├── use-timeline-history.ts # Undo/redo
│ └── use-timeline-*.ts # Other specialized hooks
├── stores/
│ ├── use-timeline-store.ts # Main timeline state
│ └── use-zoom-store.ts # Zoom state persistence
├── utils/
│ └── gap-utils.ts # Gap calculation utilities
├── types.ts # TypeScript definitions
└── constants.ts # Configuration constants
Props
The Timeline component accepts the following props:
Prop | Type | Default | Description |
---|---|---|---|
Data | |||
tracks | TimelineTrack[] | - | Required. Array of timeline tracks containing items |
totalDuration | number | - | Required. Total timeline duration in seconds |
currentFrame | number | 0 | Current playhead position frame |
fps | number | 30 | Frames per second for timeline calculations |
Event Handlers | |||
onFrameChange | (frame: number) => void | - | Called when playhead position changes |
onItemMove | (itemId: string, newStart: number, newEnd: number, newTrackId: string) => void | - | Called when an item is moved |
onItemResize | (itemId: string, newStart: number, newEnd: number) => void | - | Called when an item is resized |
onItemSelect | (itemId: string) => void | - | Called when an item is selected |
onDeleteItems | (itemIds: string[]) => void | - | Called when items are deleted |
onDuplicateItems | (itemIds: string[]) => void | - | Called when items are duplicated |
onSplitItems | (itemId: string, splitTime: number) => void | - | Called when an item is split |
onTracksChange | (tracks: TimelineTrack[]) => void | - | Called when tracks are modified |
onAddNewItem | (item: Partial<TimelineItem> & { trackId: string; start: number; end: number }) => void | - | Called when a new item is added |
Selection | |||
selectedItemIds | string[] | [] | Array of currently selected item IDs |
onSelectedItemsChange | (itemIds: string[]) => void | - | Called when selection changes |
UI Controls | |||
showZoomControls | boolean | false | Show zoom in/out controls |
showPlaybackControls | boolean | false | Show play/pause controls |
showTimelineGuidelines | boolean | true | Show alignment guidelines during drag |
showUndoRedoControls | boolean | false | Show undo/redo buttons |
showAspectRatioControls | boolean | false | Show aspect ratio selector |
Playback | |||
isPlaying | boolean | false | Current playback state |
onPlay | () => void | - | Called when play is triggered |
onPause | () => void | - | Called when pause is triggered |
playbackRate | number | 1 | Current playback speed multiplier |
setPlaybackRate | (rate: number) => void | - | Called when playback rate changes |
Track Management | |||
autoRemoveEmptyTracks | boolean | true | Automatically remove tracks when they become empty |
onAutoRemoveEmptyTracksChange | (enabled: boolean) => void | - | Called when auto-remove setting changes |
enableTrackDrag | boolean | true | Allow dragging items between tracks |
enableMagneticTrack | boolean | true | Enable magnetic snapping to other items |
enableTrackDelete | boolean | true | Allow deleting tracks |
Behavior | |||
hideItemsOnDrag | boolean | true (desktop), false (mobile) | Hide other items during drag operations |
Undo/Redo (External) | |||
canUndo | boolean | false | Whether undo action is available |
canRedo | boolean | false | Whether redo action is available |
onUndo | () => void | - | Called when undo is triggered |
onRedo | () => void | - | Called when redo is triggered |
Aspect Ratio | |||
aspectRatio | AspectRatio | - | Current aspect ratio setting |
onAspectRatioChange | (ratio: AspectRatio) => void | - | Called when aspect ratio changes |