Skip to main content

App State Subscriptions

Use kittl.appStore.subscribe() to react to real-time changes in the app state — selection changes, active tool switches, viewport updates, and more.

Subscribing to a Single Property

Pass an array with one path and a callback that receives the current value.

const unsub = await kittl.appStore.subscribe(
['selectedObjectsIds'],
(ids) => {
console.log('selected:', ids);
}
);

Subscribing to Multiple Properties

Pass multiple paths in the array. The callback receives one argument per path, in the same order.

const unsub = await kittl.appStore.subscribe(
[
'selectedObjectsIds',
'activeTool',
'viewport',
],
(ids, tool, viewport) => {
console.log('selection:', ids);
console.log('tool:', tool);
console.log('viewport:', viewport);
}
);

The callback fires whenever any of the subscribed values changes.

Unsubscribing

subscribe() returns an unsubscribe function. Call it when you no longer need updates.

const unsub = await kittl.appStore.subscribe(
['selectedObjectsIds'],
(ids) => console.log(ids)
);

// Later, when done:
unsub();

Valid Paths

Each path is a dot-separated string. The first segment must be one of the top-level app state keys:

KeyDescription
selectedObjectsIdsIDs of top-level selected objects
selectedLayersIdsIDs of selected layers
selectedArtboardIdsIDs of selected artboards
selectedArtboardsObjectIdsObject IDs within selected artboards
allSelectedObjectsIdsAll selected IDs including nested groups
canvasModeCurrent canvas mode ("erase", "crop", "inpaint", etc.)
activeToolCurrently active tool ("move", "pen", "hand", etc.)
viewportViewport position and zoom state
highlightedObjectIdID of the currently hovered object

You can drill into nested properties using dot notation:

viewport.coords.tl.x
activeTool.type

Invalid paths — empty segments, unknown top-level keys — throw an error.

Handling undefined

A callback argument is undefined when the path doesn't resolve to a value. Always account for undefined.

const unsub = await kittl.appStore.subscribe(
['activeTool'],
(tool) => {
if (tool === undefined) {
console.log('No active tool');
return;
}
console.log('tool:', tool);
}
);

Example: React to Selection Changes

const unsub = await kittl.appStore.subscribe(
['selectedObjectsIds'],
async (ids) => {
if (!ids || ids.length === 0) {
hidePropertiesPanel();
return;
}

// Fetch details for the first selected object
const result = await kittl.design.object.getObject({ id: ids[0] });
if (result.isOk) {
showPropertiesPanel(result.result);
}
}
);

Example: Track Viewport and Tool Together

const unsub = await kittl.appStore.subscribe(
['viewport', 'activeTool'],
(viewport, tool) => {
if (viewport) {
updateMinimap(viewport);
}
if (tool) {
updateToolbarHighlight(tool);
}
}
);