VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
Getting started
InstallationQuick startCore conceptsYour first video
Builder
Builder APITime formatsParallel & wait
Layers
TextImageVideoAudioCaptionsShapeGroups
Animation
Animate & keyframesEasing functionsTransitionsEffects
Renderers
Browser rendererServer rendererDOM preview
React Video Editor
QuickstartThemingUploadsCustom panelsHooks & commandsKeyboard shortcuts
API reference
Overview@videoflow/core@videoflow/renderer-browser@videoflow/renderer-server@videoflow/renderer-dom@videoflow/react-video-editor

Hooks & commands

The editor exposes its Zustand store via hooks. For reads, call useEditor(selector) — it subscribes only to the slice you pick. For writes that should be undoable, dispatch commands instead of calling setState directly.

Reading state

import { useEditor } from '@videoflow/react-video-editor';

function LayerCount() {
  const count = useEditor((s) => s.video.layers.length);
  return <span>{count} layers</span>;
}
HookReturns
useEditor(selector)Memoised slice of the store.
useVideo()The current VideoJSON.
useSelection(){ ids: string[], layers: LayerJSON[] } — selected ids and the resolved layers.
usePlayhead(){ frame, playing } — current frame and play state.
useViewport()Timeline zoom / scroll state.
useHistoryState(){ canUndo, canRedo }.
useActiveLayers()LayerJSON[] — layers currently in scope (respects the active group context).
useActiveGroup()LayerJSON | null — the group the user has drilled into.
useActiveGroupChain()LayerJSON[] — full ancestor chain of the active group.
useActiveGroupPath()string[] — the active group ids, root-to-leaf.
useEditorStore()Raw Zustand store for escape hatches.

Commands

A command is a pure function that returns an Immer patch plus metadata. Dispatching a command pushes a history entry (undoable, merge-aware) and triggers onChange.

import { useEditorStore, commands } from '@videoflow/react-video-editor';

function NudgeRight() {
  const dispatch = useEditorStore((s) => s.dispatch);
  return (
    <button onClick={() => dispatch(commands.moveLayersCommand({ ids, dx: 10 }))}>
      Move right 10px
    </button>
  );
}

Available commands

CommandUse for
addLayerCommandInsert a layer.
removeLayersCommandDelete one or more layers.
reorderLayersCommandChange stacking / track order.
moveLayersCommandTranslate by dx / dy.
resizeLayerCommandChange width / height.
trimStartCommandTrim a clip's start.
setPropertyCommandSet any layer property.
unsetPropertyCommandReset to default.
setSettingCommandPatch project settings.
setLayerEnabledCommandToggle a layer on/off.
set3DTransformCommandSet 3D translate / rotate.
setKeyframeCommand / removeKeyframeCommandEdit keyframes.
setTransitionCommandSwap transitionIn / Out.
addEffectCommand / removeEffectCommand / moveEffectCommandManage effects stack.
setEffectParamCommandPatch one effect param.
setProjectSettingsCommandPatch the project metadata.
setTrackSettingsCommandPatch per-track metadata.

History model

  • Up to 200 entries retained per session.
  • Merges within an 800 ms window — rapid inputs collapse into one undo step.
  • Cmd+Z / Cmd+Shift+Z hooked up automatically.

Renderer bridge

For advanced programmatic control, reach the underlying DomRenderer via state.bridge:

const { bridge } = useEditorStore.getState();
await bridge.seek(120);
await bridge.renderFrame(120);
VideoFlow

Open-source toolkit for composing videos from code.

Product

CoreRenderersReact Video EditorPlayground

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContact

Legal

TermsPrivacy
© 2026 VideoFlow. Apache-2.0 core.