Skip to main content

User settings

UserSettingsState is an opaque key-value store that survives chart layout switches and reloads. With persistence enabled, the SDK loads it before chart readiness and autosaves changes made through the chart API.

interface UserSettingsState {
values: Record<string, unknown>;
metadata?: Record<string, unknown>;
}

Understand what storage does

The storage layer round-trips values and emits user-settings-change. It does not infer what an arbitrary key means or automatically apply host-owned values to an order ticket, watchlist, panel, or chart option.

Use an application namespace and connect each host-owned key to its consumer:

const key = 'my-app.orderTicket.defaultQuantity';

chart.setUserSetting(key, 100);

const quantity = chart.getUserSettings().values[key];
if (typeof quantity === 'number') {
orderTicket.setDefaultQuantity(quantity);
}

Built-in SDK subsystems may use their documented settings keys. Do not place host data in an SDK-owned namespace.

Enable the automatic lifecycle

const mounted = sdk.chart.mount({
mount: '#chart',
symbol,
interval: '1D',
datafeed,
storage,
features: {
userSettings: true,
},
});

const widget = await mounted.ready();
const chart = widget.chart();

Before ready(), the controller calls loadUserSettings. A stored UserSettingsState replaces the current values wholesale. null means no saved settings, so the current initial values remain.

After readiness, these calls update memory, emit an event, and queue autosave:

chart.setUserSetting('my-app.compactMode', true);
chart.removeUserSetting('my-app.compactMode');
chart.setUserSettings({
values: {
'my-app.compactMode': false,
'my-app.orderTicket.defaultQuantity': 100,
},
});

Autosaves are microtask-coalesced, but separate in-flight writes can overlap. Serialize writes in a custom adapter or define last-write-wins ordering on the backend.

features.userSettings: { enabled: true, persistence: false } keeps the in-memory getters, setters, and change events, but disables the persistence surface. It is not a manual-save mode.

Connect persistence

Automatic calls include chart, symbol, and interval context but are not guaranteed to include a user id. Bind authenticated identity in the adapter closure or configure it as an adapter default; never authorize from a browser-supplied context value.

Use the REST chart storage adapter when your backend follows the built-in HTTP contract. Use a custom chart storage adapter for different routes or transports. This page owns the settings lifecycle and key semantics; the backend integration pages own transport, authentication, and response validation.

Observe and delete values

const unsubscribe = chart.on('user-settings-change', (event) => {
applyHostSettings(event.payload.values);
});

await chart.deleteUserSettings({ keys: ['my-app.compactMode'] });
await chart.deleteUserSettings(); // Delete all persisted and in-memory values.

unsubscribe();

Automatic load and save failures are reported as recoverable adapter errors; the chart keeps its current values. Direct persistence calls reject when they fail.

Keep chart layout and hotkey state separate

chart.getState() includes user settings as part of the complete runtime snapshot. chart.getLayout() excludes them because a chart layout is workspace state, not durable user preference state.

Keyboard shortcuts use HotkeyStorageAdapter, not ChartStorageAdapter. See Widget hotkeys.

Verify the lifecycle

  1. Mount with a saved value and confirm it is available immediately after ready().
  2. Change one key and confirm one autosave contains the complete current state.
  3. Reload with the same authenticated scope and confirm the value returns.
  4. Delete one key, then all keys, and verify both memory and backend state.
  5. Mount as another user and workspace; neither may receive the first state.
  6. Return malformed JSON, deny access, and fail a save; each must remain distinguishable from null.

Next steps