Toolbar action
Buttons in the top toolbar — registration shape, grouping, icons.
A toolbar action is a button at the top of the editor — same row as the project title and the Save/Export buttons. Use it for global commands that don't depend on a selection (e.g. "Add audio track", "Open settings", "Log state for debugging").
Registration shape
interface ToolbarAction {
id: string
label: string
icon?: ComponentType<{ className?: string }>
onClick: () => void
order: number
group?: string
}Registering one
ctx.registerToolbarAction({
id: 'mycompany:open-settings',
label: 'Open Settings',
icon: ctx.icons.settings,
order: 100,
group: 'tools',
onClick: () => {
// Open your settings UI — typically toggle local state on a parent component.
},
})Field reference
id
Unique. The registry de-duplicates by id. Namespace it (vendor:action).
label
Used as the tooltip and accessible name. Keep it short — toolbar real estate is tight.
icon
Optional. If omitted the button renders text-only. Pull from ctx.icons to respect user overrides.
onClick
A closure with access to ctx.store. The most common pattern:
onClick: () => {
const state = ctx.store.getState()
state.addTrack({ name: 'Audio 2', kind: 'audio', order: state.timeline.tracks.length, locked: false, muted: false, hidden: false })
}For anything more involved, dispatch through a separate function (so the same logic is reachable from a hotkey or context menu):
import { openSettings } from './actions'
onClick: () => openSettings(ctx.store),order
Sort key within the toolbar. Built-in toolbar actions use 1 to 20. Pick 50+ to slot after them.
group
Optional. The built-in toolbar renders groups separated by a divider. Common groups: 'tools',
'view', 'export'. Plugins typically use 'tools'.
When to use a toolbar action vs alternatives
| Use a toolbar action when | Use a context menu when | Use a hotkey when |
|---|---|---|
| The command is global (no selection needed) | The command applies to specific item(s) | Power users want it without leaving the keyboard |
| Discoverability matters (it should be visible) | Discoverability is OK from the right-click menu | Discoverability isn't required |
| It's a frequently used command | It's specific to a single item type | It's a fast repetition action |
A heavy plugin may register the same action across all three — once as a toolbar button (visible), once as a context menu item (item-scoped), and once as a hotkey (fast).
Custom toolbars
If you're building a custom editor shell, render toolbar actions via useToolbarActions():
import { useToolbarActions } from '@studio-dev/vsdk'
function CustomToolbar() {
const actions = useToolbarActions()
return (
<div className="flex items-center gap-1">
{actions.map((a) => {
const Icon = a.icon
return (
<button key={a.id} onClick={a.onClick} aria-label={a.label}>
{Icon ? <Icon className="size-4" /> : a.label}
</button>
)
})}
</div>
)
}useToolbarActions() returns the array sorted by order.