Everything a host page can use to configure, drive and listen to an embedded chart. Three layers, each built on the one before:
- The chart page: URL options and page configuration of
apps/web/index.html. - 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.
@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(orembed=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:
<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:
- A
parentOriginthat is not a barehttp(s)origin (a path, query, credentials,*,null, another scheme) disables the bridge. - With
embed-allowed-originsset:parentOriginmust be on the list;- without
parentOrigin, a list of exactly one origin is used; - a longer list without
parentOriginuses the origin the browser reports for the parent (location.ancestorOrigins, where available) if it is on the list; otherwise the bridge is disabled.
- Without the list,
parentOriginis used as given (development only). No list and noparentOrigindisables the bridge. - 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.
// 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 asre. Use a fresh one per command.payload: a plain object.nullor 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 inlistSymbols(); otherwiseunknown_symboland nothing reaches the feed. The chart then requests history for it; listen forsymbolChange, or pollgetState().candleCount, to know when it is drawn.setTimeframe: a name fromlistTimeframes()or its seconds. Unsupported seconds areunknown_timeframe.setChartType: setting a line-family type (Line,Areaand the other line variants) switches the pane toS1. 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:paneis'overlay'(drawn on the price pane) or'separate'(its own pane below).defaultsis the full default settings object;rulesholds limits for some numeric settings, for example{ period: { min: 1, max: 100 } }(some also carryprecision). A rule applies to every setting with that key, at any depth.addIndicator:settingsoverride the type's defaults and are deep-merged onto them. Only keys present in the defaults are accepted (plus a booleanshowon a line), each with the default's type: numbers must be finite and inside theruleslimits, 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 returnedidlooks likeext-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:toolis the name to pass tostartDrawing('TREND_LINE','FIBONACCI_RETRACEMENT', ...),typethe engine's numeric id,labelthe display name.groupandgroupLabelare the groups of the chart's own drawings panel, in its order:lines,channels,fib,gann,pitchfork,patterns,shapes,notes,measure.startDrawing:toolmust be a name fromlistDrawingTools()(unknown_drawing_toolotherwise).placementis'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 anotherstartDrawingends the placement; a two-point drawing that has only its first point is removed. The reply comes at once; follow the placement throughdrawingChange.'auto'adds the drawing at once at a default position in the visible area, as the chart's drawings panel does, and returns itsid.
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. Itsidis a request id until the feed server confirms the drawing.cancelDrawing:cancelledisfalsewhen no click placement was waiting;toolis the tool that was being placed, ornull.listDrawings:toolandlabelas inlistDrawingTools();typeis the numeric tool id.getCandles:countis 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[]ornot_ready.Indicators and drawings live in the page: they do not survive a reload of the chart page. Re-apply them after
ready.
ChartState#
{
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,listThemesandsetTheme) 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 withbusy. 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 withtimeout. readycomes 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 sendpinguntil it answers, ascreateKandloChart()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/>#
<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.
chartTypeis applied beforetimeframe; after a line-family type pins the pane toS1, the component sends yourtimeframeagain.- Props are not forced back. When the user picks another symbol in the
chart's toolbar,
onSymbolChangereports it and the chart keeps it until yoursymbolprop 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.jsstarts 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 readswindowordocumentwhen 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:
chart.on('crosshair', (e) => e.candle?.close); // e: CrosshairEvent