Skip to main content

Customization Precedence

TradeScript customization is layered and explicit. When two layers set the same chart property, the later/higher layer wins.

TradeScript chart after a complete light theme has been applied while retaining the same chart state
The visible result merges theme, chart/style overrides, restored layout state, host-applied user settings, and runtime calls.
PrecedenceLayerNotes
1Runtime calls on chart.customization()Immediate and highest priority until a later runtime call or restored state changes the same field.
2chart.setState(...) and chart.applyLayout(...)Restored chart state applies state.customization.
3initialState.customizationConstructor-time customization for saved state and application defaults.
4Constructor options such as theme, locale, and timezoneUsed when initialState.customization does not define the same field.
5ChartStorageAdapter user settingsUser settings are a separate state surface. They do not silently rewrite chart overrides unless host code applies them.
6Chart defaultsBuilt-in chart defaults.

Visual layer order

That ranking answers where a setting came from. Within the resulting visual state, the layers compose in a different order, lowest first:

OrderLayerSupplies
1Theme (theme / setTheme / applyTheme)A complete canvas and chrome: candles, grid, axes, crosshair, pane separators, and every --ts-chart-* token.
2chartOverridesContainer, main series, panes, scales, grid, axes, sessions, trading overlays.
3styleOverridesBackground, crosshair, indicators, drawings, handles, candles, and the low-level styles escape hatch.

A preset therefore fills in everything, and any override you set explicitly still wins. Only the keys a layer actually sets are emitted, so a partial override never blanks the values beneath it.

Three common conflicts

ConflictWinnerWhy
Constructor theme vs restored state themeRestored state (initialState.customization)Restored state is applied at a higher layer than constructor options
Runtime override vs restored layout vs later runtime callThe most recent runtime callLayer 1 wins, but a restore in between replaces the field until the next runtime call
Theme values vs explicit overridesThe explicit overridechartOverrides and styleOverrides compose above the theme

Walkthrough 1: constructor theme vs restored state theme

sdk.chart.mount({
mount: '#chart',
symbol: 'AAPL',
interval: '1D',
datafeed,
theme: 'light',
initialState: {
customization: {
theme: 'dark',
},
},
});
  • Both layers set the theme: the constructor option says 'light' (layer 4), the seeded customization says 'dark' (layer 3).
  • Winning value: theme = 'dark'. The chart renders dark from the first paint.
  • Reason: initialState.customization is restored state, and restored state outranks constructor options for the same field. The theme: 'light' option would only apply if initialState.customization did not define theme.

Walkthrough 2: runtime override vs restored layout vs later runtime call

Assume savedLayout carries customization.chartOverrides.grid.horizontal.color = '#0f172a'.

const ui = chart.customization();

ui.applyOverrides({ grid: { horizontal: { color: '#1f2937' } } });
// grid is now #1f2937 — runtime call, layer 1

await chart.applyLayout(savedLayout);
// grid is now #0f172a — the restore replaced the field, layer 2 applied later

ui.applyOverrides({ grid: { horizontal: { color: '#334155' } } });
// grid is now #334155 — a later runtime call wins again
  • Winning value: grid.horizontal.color = '#334155'.
  • Reason: runtime calls hold a field only until a later restore or runtime call changes the same field. The restore legitimately overwrote #1f2937 with #0f172a; the final runtime call then took the field back. If your product theme must always win after a layout restore, reapply it after applyLayout(...) — see My restored layout uses the old theme.

Walkthrough 3: theme values vs explicit overrides

chart.customization().applyTheme('ocean-depths');
chart.customization().applyOverrides({
mainSeries: { upColor: '#16a34a', downColor: '#dc2626' },
});
  • Both layers color the candles: the ocean-depths preset supplies its own main-series colors, and the override pins #16a34a / #dc2626.
  • Winning values: mainSeries.upColor = '#16a34a', mainSeries.downColor = '#dc2626'. Everything not overridden — background, grid, axes, chrome tokens — adopts ocean-depths.
  • Reason: within the visual composition, chartOverrides sits above the theme. Switching themes later still keeps the pinned candle colors until the override is cleared; the same holds for candle colors set through setCandleColors(...).

Layouts

Layouts carry ChartState, including customization. Applying a layout calls the same state path as setState(...); it does not introduce a second customization authority.

const layout = chart.getLayout('desk-open');
await chart.applyLayout(layout);

User Settings

features.userSettings: true enables load and autosave through the configured storage adapter. The stored UserSettingsState is separate from CustomizationState.

Use user settings for product preferences such as favorite intervals, menu state, or host-defined preferences. Use initialState.customization, applyOverrides(...), applyStyleOverrides(...), and the other customization methods for chart appearance and behavior.

This separation is intentional: customization remains typed and inspectable instead of being silently overridden by browser or server preferences. A stored user preference only affects chart customization when host code reads it and issues a customization call — at which point that call is an ordinary layer-1 runtime call.

Provenance Inspection

Use native snapshot tracking when a host needs to explain which layer last changed a customization field:

const tracker = chart.customization().trackSnapshots('constructor');

chart.customization().applyOverrides({
grid: { horizontal: { color: '#1f2937' } },
});

const inspection = tracker.inspect();
console.table(inspection.effectiveFields);
tracker.dispose();

inspection.effectiveFields lists current leaf fields and the snapshot source that last changed each one. inspection.fieldHistory keeps set/clear history. inspection.fieldApplication reports whether a field is applied, conditional, application-owned, stored-only, or unknown. inspection.diagnostics reports invalid typed values.

For saved-state debugging, use the exported inspectCustomizationSnapshots(...), inspectCustomizationFieldApplication(...), inspectChartDisplaySettingsApplication(...), inspectBuiltInIndicatorDefaults(...), inspectBuiltInIndicatorProperties(...), validateCustomizationState(...), and validateChartDisplaySettings(...) helpers. They accept public state objects and return structured results suitable for logs or support reports.