kandlo Docs

3. API reference

Last updated · 19 min read

On this page

Everything a host page can use to configure, drive and listen to an embedded chart. Three layers, each built on the one before:

  1. The chart page: URL options and page configuration of apps/web/index.html.
  2. The postMessage protocol: the messages the chart's embed bridge accepts and sends. Use it directly from a native web view or a page without npm.
  3. @kandlo/react: <KandloChart/>, createKandloChart() and the TypeScript types.

The sources this page describes: apps/web/src/interactions/embed-bridge.js, apps/web/src/interactions/embed-protocol.js, packages/kandlo-react/src/*.js and packages/kandlo-react/*.d.ts. Unit tests: npm test.

Times are unix seconds everywhere. Timeframes are names ('M5') on the API and seconds (300) on the wire to the feed.

Commands at a glance#

The same operation in each layer. A dash means the layer has no shortcut for it; use the handle (the component's ref) or request().

To React prop ChartHandle method postMessage type
Check the bridge answers - ping() ping
Read the pane's state - getState() getState
List or change the symbol symbol listSymbols(), setSymbol(s) listSymbols, setSymbol
List or change the timeframe timeframe listTimeframes(), setTimeframe(tf) listTimeframes, setTimeframe
List or change the chart type chartType listChartTypes(), setChartType(t) listChartTypes, setChartType
List or change the theme theme listThemes(), setTheme(id) listThemes, setTheme
List or change the layout layout listLayouts(), setLayout(l) listLayouts, setLayout
Manage indicators indicators listIndicatorTypes(), listIndicators(), addIndicator(), updateIndicator(), removeIndicator(), clearIndicators() same names
Manage drawings - listDrawingTools(), startDrawing(), cancelDrawing(), listDrawings(), removeDrawing(), clearDrawings() same names
Read candles - getCandles({ count }) getCandles
Crosshair and live-candle streams onCrosshair, onCandle on('crosshair'), on('candle') subscribe
Hide the chart's toolbar chrome="minimal" option chrome: 'minimal' URL chrome=minimal

1. The chart page#

1.1 Chart page URL options#

The embed bridge starts only when both hold:

  • the page is inside a frame (window.self !== window.top), and
  • the URL has embed=1 (or embed=true).

A top-level visit, or a frame without embed=1, is the normal standalone chart: no listener, no style change, no URL option below is read.

Parameter Value Effect
embed 1 Required.
parentOrigin an origin, https://app.example.com The one origin the bridge talks to. See 1.3.
chrome minimal Hides the chart's toolbar, zoom selector and tuning button. Any other value keeps them.
synth 1 Keeps the generated history backfill (for demos on recorded data). Off by default in an embed.
symbol a symbol from the feed Initial symbol.
chartType a chart type name, Candles Initial chart type. Applied before timeframe.
timeframe a timeframe name, M5 Initial timeframe (names only in the URL; the client libraries convert seconds).
theme a theme id, dark-blue Initial theme.

symbol, chartType and timeframe are checked like the matching commands once the feed's symbol list has arrived. An invalid value is skipped with a console warning ([embed] ignored ?symbol=: ...); the others still apply.

In an embed, the chart type, timeframe and theme the host sets are not saved in the chart origin's localStorage, and the values saved by the standalone chart on the same origin are not restored over the host's choice.

The standalone page also understands replay=1: it plays the recorded session in apps/web/assets/fixtures/ instead of connecting to a feed (one recorded series, relabelled to whatever symbol is asked for). Pass it through params: { replay: 1 } from the React package.

1.2 Page configuration#

Set in apps/web/index.html, in the inline script that defines var AppData = {...}, which runs before app.js.

Key Type Default Meaning
AppData['embed-allowed-origins'] array of origins, or one comma/space separated string unset The host origins allowed to drive the chart. When set, it is the authority (see 1.3). Entries that are not bare http(s) origins are dropped.
AppData['relay-servers-list'] [{ server, name }] [{ server: 'ws://localhost:8443', name: 'Local relay' }] Feed servers. See Serve your own market data.
AppData['is-relay-feed'] 1 1 Keep 1; it selects relay-servers-list.
AppData['serverTimeZoneOffset'] minutes 0 Leave at 0: the feed sends true unix times. The time axis, crosshair and clock show time + (userTimeZoneOffset - serverTimeZoneOffset) minutes. If the key is removed the chart assumes 120, which shifts every label by two hours.
AppData['userTimeZoneOffset'] minutes east of UTC unset (the viewer's own time zone) The zone the axis is shown in. Unset: each viewer sees local time. 0: everyone sees UTC. 60: UTC+1, and so on (a fixed offset; daylight saving is not applied).

Example, added right after the var AppData = {...}; line:

HTML
<script>
  AppData['embed-allowed-origins'] = ['https://app.example.com'];
  AppData['relay-servers-list'] = [{ server: 'wss://feed.example.com', name: 'Primary' }];
</script>

Feature switches on window.__kiFeature (read each time they are needed, so they can be set in that same inline script):

Key Embed default Standalone default Meaning
synthBackfill false (true with synth=1) true false stops generated prices from filling history the feed did not deliver within 1.5 seconds.
persistChartSettings false true false neither restores nor saves the chart type and timeframe in localStorage.

In an embed the bridge sets both keys when it starts, over any value from the inline script; the URL option synth=1 is the way to keep the backfill there.

1.3 Which parent origin the bridge accepts#

The bridge decides once, at start, the single origin it exchanges messages with:

  1. A parentOrigin that is not a bare http(s) origin (a path, query, credentials, *, null, another scheme) disables the bridge.
  2. With embed-allowed-origins set:
    • parentOrigin must be on the list;
    • without parentOrigin, a list of exactly one origin is used;
    • a longer list without parentOrigin uses the origin the browser reports for the parent (location.ancestorOrigins, where available) if it is on the list; otherwise the bridge is disabled.
  3. Without the list, parentOrigin is used as given (development only). No list and no parentOrigin disables the bridge.
  4. Where the browser reports the real parent origin, it must equal the chosen origin, or the bridge is disabled.

A disabled bridge logs [embed] bridge disabled: <reason> in the chart page's console and answers nothing. The client libraries then fail with timeout.

After that, an incoming message is processed only if it comes from window.parent and from the chosen origin. Everything the bridge sends goes to window.parent with that origin as targetOrigin, never '*'.


2. The postMessage protocol#

2.1 Envelope#

All messages are plain objects (structured clone), protocol version 1.

JavaScript
// host -> chart: a command
{ kandlo: 1, id: 'k1', type: 'setSymbol', payload: { symbol: 'EURUSD' } }

// chart -> host: the reply
{ kandlo: 1, re: 'k1', ok: true,  result: { chartId: 'chart-1', symbol: 'EURUSD' } }
{ kandlo: 1, re: 'k1', ok: false, error: { code: 'unknown_symbol', message: 'unknown symbol: EURUSDX' } }

// chart -> host: an event (no `re`)
{ kandlo: 1, event: 'symbolChange', payload: { chartId: 'chart-1', symbol: 'EURUSD' } }
  • id: a non-empty string of at most 64 characters, or a safe integer. It is echoed as re. Use a fresh one per command.
  • payload: a plain object. null or a missing payload is read as {}.
  • Every command gets exactly one reply.

How the bridge treats malformed input:

Input Outcome
Not a plain object, or kandlo !== 1 Ignored, no reply.
Missing or invalid id Dropped, no reply (there is nothing to reply to).
Unknown type unknown_command
payload not an object bad_payload
A payload field the command does not take bad_payload (unexpected field: ...). Unknown fields are refused, never ignored.

Results and event payloads are copied through JSON, so they contain only JSON values.

2.2 Panes#

A layout shows one to four chart panes, chart-1 to chart-4. Commands that accept chartId default to the active pane, the first visible one; with the default single-pane layout that is always chart-1. A chartId that is not on screen is refused with unknown_chart.

2.3 Commands#

Command Payload Result
ping {} { pong: true, protocol: 1, version: '1.0.0' }
getState { chartId? } ChartState of that pane
subscribe { crosshair?: boolean, candle?: boolean } the current flags, { crosshair, candle }
listSymbols {} [{ symbol, label, type, digits, active, timeframes }]; timeframes in seconds, from the feed
setSymbol { symbol, chartId? } { chartId, symbol }
listTimeframes {} [{ timeframe: 'S1', seconds: 1 }, ...], 15 entries from S1 to D1
setTimeframe { timeframe, chartId? } or { seconds, chartId? } (not both) { chartId, timeframe, seconds }
listChartTypes {} ['Line', 'Area', ..., 'Candles', ...], 47 names
setChartType { chartType, chartId? } { chartId, chartType }
listThemes {} theme ids, 37 of them ('light', 'dark-blue', 'midnight', ...)
setTheme { theme } { theme }
listLayouts {} [{ layout: 'FULL', charts: 1 }, { layout: 'VERTICAL_2', charts: 2 }, ...], 13 entries
setLayout { layout } { layout }
listIndicatorTypes {} [{ type, label, pane, defaults, rules }], 147 entries
listIndicators { chartId? } [{ id, type, chartId, visible, settings }]
addIndicator { type, settings?, chartId? } { id, type, chartId }
updateIndicator { id, settings } { id }
removeIndicator { id } { id }
clearIndicators { chartId? } { chartId }
listDrawingTools {} [{ tool, type, label, group, groupLabel }], 60 entries
startDrawing { tool, placement?, chartId? } { tool, chartId, placement }, plus id for placement: 'auto'
cancelDrawing {} { cancelled, tool }
listDrawings { chartId? } [{ id, type, tool, label, chartId, symbol, locked }]
removeDrawing { id } (string or integer) { id }, the engine's id with its original type
clearDrawings { chartId? } { chartId }
getCandles { count?, chartId? } the last count candles of the pane, oldest first: [{ time, open, high, low, close, volume? }]

Notes per command:

  • setSymbol: the symbol must be in listSymbols(); otherwise unknown_symbol and nothing reaches the feed. The chart then requests history for it; listen for symbolChange, or poll getState().candleCount, to know when it is drawn.

  • setTimeframe: a name from listTimeframes() or its seconds. Unsupported seconds are unknown_timeframe.

  • setChartType: setting a line-family type (Line, Area and the other line variants) switches the pane to S1. Set the chart type first and the timeframe second. The React component does this ordering for you.

  • setTheme: applied to the whole page, chart and surrounding UI. Not saved in an embed.

  • setLayout: new panes open on the current symbol. Panes a smaller layout hides are no longer addressable.

  • listIndicatorTypes: pane is 'overlay' (drawn on the price pane) or 'separate' (its own pane below). defaults is the full default settings object; rules holds limits for some numeric settings, for example { period: { min: 1, max: 100 } } (some also carry precision). A rule applies to every setting with that key, at any depth.

  • addIndicator: settings override the type's defaults and are deep-merged onto them. Only keys present in the defaults are accepted (plus a boolean show on a line), each with the default's type: numbers must be finite and inside the rules limits, strings at most 64 characters. Arrays and other non-scalar values cannot be set. At most 3 levels of nesting and 64 keys per object. At most 30 indicators per pane (limit_reached). The returned id looks like ext-mvdmt4ud-1. Example:

    JavaScript
    { type: 'rsi', settings: { period: 21, lines: { main: { color: '#ffaa00' } } } }
  • updateIndicator: the same checks against the indicator's type; the values are deep-merged into its current settings.

  • listDrawingTools: tool is the name to pass to startDrawing ('TREND_LINE', 'FIBONACCI_RETRACEMENT', ...), type the engine's numeric id, label the display name. group and groupLabel are the groups of the chart's own drawings panel, in its order: lines, channels, fib, gann, pitchfork, patterns, shapes, notes, measure.

  • startDrawing: tool must be a name from listDrawingTools() (unknown_drawing_tool otherwise). placement is 'click' (default) or 'auto'.

    • 'click' arms the pane: the pointer turns into a crosshair and the next click on that pane creates the drawing with the tool's default style and moves it so its first point is under the click. A two-point tool (trend line, ray, rectangle, Fibonacci retracement, ...) then follows the pointer with its second point until a second click fixes it. Other tools are complete after the first click, at their default size (shrunk toward the click when that size would leave the visible area). The clicks that place are kept from the chart (they neither pan nor select). Escape in the chart, cancelDrawing, or another startDrawing ends the placement; a two-point drawing that has only its first point is removed. The reply comes at once; follow the placement through drawingChange.
    • 'auto' adds the drawing at once at a default position in the visible area, as the chart's drawings panel does, and returns its id.

    The chart allows at most 15 drawings per symbol, counted over every pane showing that symbol (limit_reached). Once placed, a drawing is moved and edited in the chart like one added from its own panel. Its id is a request id until the feed server confirms the drawing.

  • cancelDrawing: cancelled is false when no click placement was waiting; tool is the tool that was being placed, or null.

  • listDrawings: tool and label as in listDrawingTools(); type is the numeric tool id.

  • getCandles: count is an integer from 1 to 5000, default 500. Values are market prices even for derived chart types (Heikin Ashi, Renko and so on). A pane with no data returns [] or not_ready.

  • Indicators and drawings live in the page: they do not survive a reload of the chart page. Re-apply them after ready.

ChartState#

TypeScript
{
  protocol: 1,
  version: '1.0.0',                 // the chart page's bridge version
  chart: { chartId, symbol, timeframe, seconds, chartType } | null,
  theme: string,
  layout: string | null,            // 'FULL', 'VERTICAL_2', ...
  chartIds: string[],               // panes on screen, in layout order
  activeChartId: string,
  indicators: IndicatorInstance[],  // of this pane
  drawingCount: number,             // of this pane
  candleCount: number,              // candles the pane holds
}

2.4 Events#

Events are sent only after ready.

Event Payload When
ready ChartState of the active pane plus hasData Once the active pane has drawn data. If no data arrives within 30 seconds of the chart starting, it is sent anyway with hasData: false; commands work from then on. If the chart has not started 30 seconds after the frame loaded (typically: no feed connection yet), it logs [embed] the chart has not started yet; still waiting and keeps waiting: once the feed connects, the chart starts and ready follows as above. Commands sent before then are queued. Sent again if the chart page reloads inside the frame.
symbolChange { chartId, symbol } A pane's symbol changed, from any source: a command, the chart's toolbar, a URL option.
timeframeChange { chartId, timeframe, seconds } A pane's timeframe changed.
chartTypeChange { chartId, chartType } A pane's chart type changed.
themeChange { theme } The theme changed.
layoutChange { layout, chartIds } The layout changed.
crosshair { chartId, time, price, candle } The pointer moved over a pane: time of the candle under it, price at the pointer, candle its OHLC or null. At most one per animation frame, only when something changed. time: null (and price, candle null) when the pointer leaves the chart or the frame loses focus. On by default; subscribe({ crosshair: false }) turns it off.
candle { chartId, symbol, timeframe, candle, isNew } The active pane's newest candle changed (a tick arrived). At most four per second, the last change always delivered. isNew is true when this candle is newer than the one in the previous candle event. Off by default; subscribe({ candle: true }) turns it on.
drawingChange { chartId, reason, ids, count, placing, tool?, id? } reason: 'changed': the pane's drawings were added or removed, from any source (a command, the chart's own panel, a placement). 'armed': startDrawing with placement: 'click' is waiting for a click (tool). 'placed': that placement finished (tool, id). 'cancelled': it ended without a drawing (tool). ids and count are the pane's drawings after the change; placing is { tool, chartId } while a click placement waits, else null. A placement also sends 'changed' when it creates the drawing.

A pane that appears through a layout change does not send change events for its initial values; layoutChange covers it.

2.5 Error codes#

Code Meaning
bad_request Reserved for an envelope without a usable id; such a message is dropped, so a host never receives it.
bad_payload Payload is not an object, a field is missing, has the wrong type, or is not accepted by the command.
bad_setting An indicator setting does not exist, has the wrong type, or is outside its rules.
unknown_command type is not a command.
unknown_chart chartId is not a pane on screen.
unknown_symbol Not in the feed's symbol list.
unknown_timeframe Not a timeframe name, or unsupported seconds.
unknown_chart_type Not in listChartTypes().
unknown_theme Not in listThemes().
unknown_layout Not in listLayouts().
unknown_indicator Not in listIndicatorTypes().
unknown_indicator_id No indicator with that id.
unknown_drawing_id No drawing with that id.
unknown_drawing_tool Not a tool name from listDrawingTools().
limit_reached 30 indicators on the pane already, or 15 drawings on the pane's symbol.
not_ready The chart has not started yet, has no data for getCandles, or the pane has no symbol yet for a drawing.
busy More than 50 commands arrived before the chart had its store and the feed's symbol list (2.6).
engine_error The engine threw while running the command; message holds its text (at most 200 characters).

2.6 Timing#

  • Commands that need the chart (all but ping, subscribe, listThemes and setTheme) are held until the chart has its store and the feed's symbol list, then run in arrival order. Up to 50 are held; more are refused with busy. So a host can send its setup straight after the frame loads. If the chart never starts (no feed), held commands get no reply; the client libraries then reject them with timeout.
  • ready comes later, when data is drawn.
  • A message posted into the frame before the chart page has loaded is lost by the browser. Either wait for ready (the bridge sends it unprompted), or send ping until it answers, as createKandloChart() does.

3. @kandlo/react#

Installed from the tarball packages/kandlo-react/dist/kandlo-react-1.0.0.tgz (see Quickstart). React 17 or later is an optional peer dependency; the plain API needs no React. No other runtime dependencies.

3.1 Entry points and exports#

Import Exports
'@kandlo/react' KandloChart, and everything below
'@kandlo/react/vanilla' createKandloChart, buildChartUrl, timeframeName, KandloError, TIMEFRAME_SECONDS, EVENTS, DEFAULT_SANDBOX, PROTOCOL, VERSION (does not import React)
Export Value
PROTOCOL 1
VERSION '1.0.0'
DEFAULT_SANDBOX 'allow-scripts allow-same-origin'
TIMEFRAME_SECONDS { S1: 1, S5: 5, S10: 10, S15: 15, S30: 30, M1: 60, M2: 120, M3: 180, M5: 300, M10: 600, M15: 900, M30: 1800, H1: 3600, H4: 14400, D1: 86400 } (frozen)
EVENTS ['ready', 'symbolChange', 'timeframeChange', 'chartTypeChange', 'themeChange', 'layoutChange', 'crosshair', 'candle', 'drawingChange'] (frozen)
timeframeName(seconds) 'M5' for 300, null for an unsupported value
buildChartUrl(src, options, hostOrigin, baseHref?) the iframe URL: src plus embed=1, parentOrigin and the initial-state options. For hosts that build the iframe themselves. Throws KandloError('bad_option').
KandloError Error subclass with name: 'KandloError' and code

3.2 createKandloChart(element, options)#

Appends an iframe that fills element and returns a promise of a ChartHandle, resolved on the chart's first ready.

Option Type Default Meaning
src string required URL of the chart page. Must be http(s). Relative URLs resolve against the host page.
symbol string Initial symbol (URL option).
timeframe name or seconds Initial timeframe. Seconds are converted to a name; an unsupported number is bad_option.
chartType string Initial chart type.
theme string Initial theme.
chrome 'minimal' or 'full' 'full' 'minimal' hides the chart's own toolbar. Other values are bad_option.
synth boolean false true keeps generated history backfill (demos only).
params object Extra query parameters for the chart page, for example { replay: 1 }. null/undefined values are skipped. The client's own keys (embed, parentOrigin, theme, symbol, timeframe, chartType, chrome, synth) are refused with bad_option.
sandbox string or false DEFAULT_SANDBOX iframe sandbox tokens; false leaves the attribute off. See Security.
allow string none iframe allow (permissions policy).
title string 'Price chart' iframe title, for assistive technology.
className string Class of the iframe element.
on { [event]: listener } Listeners attached before the frame loads, so the first ready is never missed.
signal AbortSignal Aborting destroys the chart, during start-up or later. Already aborted: rejects with destroyed.
readyTimeout ms 45000 How long to wait for the first ready.
requestTimeout ms 10000 Per-command reply timeout.
pingInterval ms 250 Handshake ping interval after the iframe's load event.

The iframe also gets referrerpolicy="strict-origin-when-cross-origin" and style="border:0; display:block; width:100%; height:100%". Give element a size.

The promise rejects with a KandloError:

Code When
bad_option Invalid options; element is not a DOM element, or its document has no window; the host page has no http(s) origin (file://, an opaque sandboxed frame).
timeout No ready within readyTimeout. The message says whether the chart page never answered (wrong src, or the host's origin is not allowed) or answered but had no data. The iframe is removed.
destroyed destroy() was called or signal aborted before ready.

3.3 ChartHandle#

Properties:

Property Type Meaning
iframe HTMLIFrameElement The frame.
origin string The chart page's origin, the only origin messages are exchanged with.
state ReadyState or null The last ready payload; its theme is kept current by themeChange.
destroyed boolean

Methods. Every command method returns a promise of the result in 2.3, or rejects with a KandloError carrying the chart's error code (2.5), timeout (no reply within requestTimeout), destroyed, or no_bridge (the frame has no window to post to).

Method Command sent
ping() ping
getState({ chartId? }?) getState
listSymbols() listSymbols
setSymbol(symbol, { chartId? }?) setSymbol
listTimeframes() listTimeframes
setTimeframe(timeframe, { chartId? }?) setTimeframe, with timeframe for a name or seconds for a number
listChartTypes() listChartTypes
setChartType(chartType, { chartId? }?) setChartType
listThemes() listThemes
setTheme(theme) setTheme
listLayouts() listLayouts
setLayout(layout) setLayout
listIndicatorTypes() listIndicatorTypes
listIndicators({ chartId? }?) listIndicators
addIndicator(type, settings?, { chartId? }?) addIndicator
updateIndicator(id, settings) updateIndicator
removeIndicator(id) removeIndicator
clearIndicators({ chartId? }?) clearIndicators
listDrawingTools() listDrawingTools
startDrawing(tool, { placement?, chartId? }?) startDrawing
cancelDrawing() cancelDrawing
listDrawings({ chartId? }?) listDrawings
removeDrawing(id) removeDrawing
clearDrawings({ chartId? }?) clearDrawings
getCandles({ count?, chartId? }?) getCandles
request(type, payload?, { timeout? }?) any command, raw. Rejects with bad_option when type is not a string or payload not a plain object.
on(event, listener) Returns an off function. Throws KandloError('bad_option') synchronously for an unknown event or a non-function listener.
off(event, listener)
destroy() Removes the iframe and every listener; pending commands reject with destroyed. Idempotent.

undefined fields are left out of the payload, so setSymbol('X', undefined) sends { symbol: 'X' }.

The crosshair and candle streams are switched on in the chart only while at least one listener for them is attached; the handle sends subscribe for you. After a reload of the chart page inside the frame, the handle restores them on the next ready.

A listener that throws is logged with console.error and does not stop the other listeners.

3.4 <KandloChart/>#

JSX
<KandloChart
  ref={chartRef}                       // ChartHandle once ready, null before and after unmount
  src="https://charts.example.com/"
  symbol="EURUSD" timeframe="M5" chartType="Candles" theme="dark-blue"
  layout="FULL"
  indicators={[{ type: 'rsi', settings: { period: 21 } }]}
  chrome="minimal"
  style={{ height: 480 }}
  onReady={(state, chart) => {}}
  onCrosshair={(e) => {}}
  onError={(err) => {}}
/>
Prop Effect
src, chrome, synth, sandbox, allow, params, readyTimeout, requestTimeout Frame options (3.2). Changing one recreates the frame. params is compared by value. on, signal and pingInterval are not props: use the callbacks below and unmounting instead.
symbol, timeframe, chartType, theme Put in the frame URL at creation; sent as a command whenever the prop changes, and only if the chart is not already showing that value.
layout Sent after ready and on every change.
indicators undefined: the component leaves indicators alone. An array of { type, settings?, chartId? }: the component keeps exactly these on the chart, adding and removing only the difference (compared by value). Indicators added through the chart's UI or the ref are not touched. Re-applied after a reload of the chart page.
title, iframeClassName Applied to the iframe without recreating it.
className, style The container <div data-kandlo-chart>. Its base style is position: relative; width: 100%; height: 100%; min-height: 300px; give it, or its parent, a height.
onReady(state, chart) First ready, and again after a reload of the chart page.
onSymbolChange, onTimeframeChange, onChartTypeChange, onThemeChange, onLayoutChange Changes from any source: props, the ref, or the user in the chart's toolbar.
onDrawingChange The drawingChange event: drawings added or removed, and click placements armed, placed or cancelled.
onCrosshair, onCandle Passing the callback turns the stream on; leaving it out turns it off.
onError(err) A command the component sent failed, or the chart did not start (timeout). Default: console.warn. destroyed errors from unmounting are not reported.

Behaviour:

  • Commands from prop changes are sent in the order the props changed, one after the other.
  • chartType is applied before timeframe; after a line-family type pins the pane to S1, the component sends your timeframe again.
  • Props are not forced back. When the user picks another symbol in the chart's toolbar, onSymbolChange reports it and the chart keeps it until your symbol prop changes.
  • Callbacks can be new functions on every render; nothing is recreated.
  • StrictMode-safe: the development double mount leaves one live iframe.
  • Server rendering: react.js starts with 'use client'. On the server the component renders only its empty container <div data-kandlo-chart>; the iframe is created in an effect, in the browser. No module of the package reads window or document when it is imported.

3.5 Client error codes#

KandloError.code is one of the chart's codes (2.5) or the client's own:

Code Meaning
bad_option Invalid option or argument, raised before anything is sent.
timeout No ready within readyTimeout, or no reply within requestTimeout.
destroyed The chart was destroyed (or the component unmounted) before the call finished.
no_bridge The iframe has no window to post to (it was removed from the document).

3.6 TypeScript#

index.d.ts (main entry) and vanilla.d.ts declare every type used above: ChartHandle, CreateChartOptions, FrameOptions, InitialState, KandloChartProps, IndicatorSpec, ChartState, ReadyState, ChartView, Candle, SymbolInfo, TimeframeInfo, LayoutInfo, IndicatorTypeInfo, IndicatorInstance, DrawingInfo, DrawingToolInfo, DrawingPlacement, StartDrawingOptions, StartDrawingResult, IndicatorSettings, CrosshairEvent, CandleEvent, DrawingChangeEvent, ChartEvents, ChartEventName, ChartListener, PaneOption, RequestOptions, Timeframe, TimeframeName, KandloErrorCode. on() is typed per event:

TypeScript
chart.on('crosshair', (e) => e.candle?.close);   // e: CrosshairEvent

Type to search all guides.