Video Studio SDKv0.0.3
UI

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 VideoStudioProvider

App-level overrides always win, so consumers can rebrand the editor regardless of which plugins are loaded.

Setting overrides from the provider

app/editor/editor.tsx
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.

my-plugin/plugin.ts
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

icons.ts
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.

On this page