Icon system
Override icons globally, look icons up at runtime, register plugin icons. Complete reference of every icon name.
The SDK uses a single keyed icon map (StudioIconMap) for every icon in the editor — playback,
toolbar, inspector, plugin sidebar, item types. The map is built from defaults (Hugeicons + a few
custom components), then merged with plugin-registered icons, then with app-level overrides.
You can replace any icon at any layer.
Override precedence
DEFAULT_ICON_MAP < plugin-registered icons < icons prop on VideoStudioProviderApp-level overrides always win, so consumers can rebrand the editor regardless of which plugins are loaded.
Setting overrides from the provider
import { Play, Pause, Save, Download } from 'lucide-react'
// IMPORTANT: define outside the component (or wrap in useMemo).
// A new object identity on every render forces the icon context to recompute.
const ICONS = {
play: Play,
pause: Pause,
save: Save,
download: Download,
}
export default function Editor() {
return (
<VideoStudioProvider icons={ICONS}>
<StudioEditor />
</VideoStudioProvider>
)
}Every icon component must accept { className?: string } — the SDK applies sizing via Tailwind
classes (e.g. size-4, size-5).
type StudioIconComponent = ComponentType<{ className?: string }>Icons you don't override fall through to the built-in defaults. You can override one, five, or all of them.
Reading icons inside your code
There are three ways to read an icon, depending on what you're doing:
<StudioIcon name="..." /> — when rendering one
import { StudioIcon } from '@studio-dev/vsdk'
<button>
<StudioIcon name="play" className="size-4" />
</button>Memoized internally, so it's safe in tight render loops.
useStudioIcon(name) — when you need the component reference
import { useStudioIcon } from '@studio-dev/vsdk'
function PlayButton() {
const PlayIcon = useStudioIcon('play')
return <PlayIcon className="size-4" />
}Use this when you need to pass the icon component to a child as a prop (e.g. a Button's icon prop)
rather than render it directly.
useIconMap() — when you need many at once
import { useIconMap } from '@studio-dev/vsdk'
function Toolbar() {
const icons = useIconMap()
return (
<>
<icons.skipBack className="size-4" />
<icons.play className="size-4" />
<icons.skipForward className="size-4" />
</>
)
}The map returned by useIconMap() is the resolved map (defaults + plugin icons + app overrides),
so it always reflects what the user sees.
Registering icons from a plugin
Plugins can add their own icons to the map via ctx.registerIcons in onRegister. Useful when your
plugin contributes a panel that needs an icon the SDK doesn't ship with.
import { createPlugin } from '@studio-dev/vsdk/plugins'
import { SparkleIcon, FlameIcon } from './icons'
export function myPlugin() {
return createPlugin({
id: 'mycompany:my-plugin',
name: 'My Plugin',
version: '0.1.0',
onRegister(ctx) {
ctx.registerIcons({
sparkle: SparkleIcon,
flame: FlameIcon,
})
ctx.registerPanel({
id: 'my-panel',
label: 'My Panel',
icon: ctx.icons.sparkle, // ← reads from the same map, so user overrides apply
component: lazy(() => import('./panel')),
order: 50,
})
},
})
}The keys you register are added to the same map components read through useIconMap(). App-level
overrides applied via the icons prop still win over plugin-registered icons of the same name —
this is the override layering working as intended.
All built-in icon names
StudioIconName is the union of every name the SDK ships defaults for. They're grouped by purpose
below — any of these can be passed to <StudioIcon name="..." /> or used as a key in the icons
prop / registerIcons call.
Playback & transport
play, pause, skipBack, skipForward, stepBack, stepForward, volumeOn, volumeOff
Track type icons
trackVideo, trackOverlay, trackAudio, trackCaptions, trackEffects
Timeline controls
magnet, zoomIn, zoomOut
General actions
add, close, delete, trash, search, copy, duplicate, paste, scissors, undo, redo,
download, upload, uploadCloud
Visibility & lock
eyeOn, eyeOff, lock, unlock, maximize, minimize
Layout & navigation
panelLeft, panelRight, chevronDown, chevronRight, chevronUp, arrowLeft, arrowUpToLine,
arrowDownToLine
Item types
itemVideo, itemAudio, itemImage, itemText, itemShape, itemCaptions, itemEffect,
itemAdjustment, itemOverlay
Inspector & properties
settings, settings2, sliders, move, link, hash, timer, clock, rotateCcw, rotateCw,
palette, paintBucket, alignLeft, alignCenter, alignRight, transition, ghost
Mask & segmentation
maskRectangle, maskEllipse, maskPolygon, pointerClick, addCircle, removeCircle, loader
Plugin sidebar slots
pluginEffects, pluginStock, pluginAi, pluginTypography, pluginElements, pluginStorage,
pluginBrandKit, pluginFilters
Miscellaneous
speed, pencil, file, helpCircle, info, mapPin, diamond, moreHorizontal,
loaderSpinner, layoutGrid, list, galleryHorizontal, galleryVertical, checkCircle,
history
Structural (used by vendored components — rarely overridden)
check, circle, save, cloudSync
TypeScript
Both the union of names and the map type are exported:
import type { StudioIconName, StudioIconMap, StudioIconComponent } from '@studio-dev/vsdk'StudioIconMap is Record<StudioIconName, StudioIconComponent> & Record<string, StudioIconComponent>
— the intersection with Record<string, …> is what lets plugins register icons under arbitrary keys
without breaking the type.
Common recipes
Swap the entire icon set for Lucide
import {
Play, Pause, ChevronRight, Search, Plus, Trash2, Lock, Unlock, Eye, EyeOff,
// …continue for every name you want to override
} from 'lucide-react'
export const ICONS = {
play: Play,
pause: Pause,
chevronRight: ChevronRight,
search: Search,
add: Plus,
delete: Trash2,
trash: Trash2,
lock: Lock,
unlock: Unlock,
eyeOn: Eye,
eyeOff: EyeOff,
} as const<VideoStudioProvider icons={ICONS}>Names you don't override keep the Hugeicons defaults.
Custom branded icon component
function BrandedPlayIcon({ className }: { className?: string }) {
return (
<div className={`flex items-center justify-center rounded-full bg-brand ${className}`}>
<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z" /></svg>
</div>
)
}
<VideoStudioProvider icons={{ play: BrandedPlayIcon }}>Add icons without overriding any
If you only want to add new icons (for your own UI), register them through a tiny "icon-only" plugin:
import { createPlugin } from '@studio-dev/vsdk/plugins'
import { MyIconA, MyIconB } from './icons'
export const myIconsPlugin = createPlugin({
id: 'mycompany:icons',
name: 'My Icons',
version: '0.1.0',
onRegister(ctx) {
ctx.registerIcons({ myIconA: MyIconA, myIconB: MyIconB })
},
})Then any component (including other plugins) can read useIconMap().myIconA.