react-klinecharts-ui is a headless React library for building financial trading terminals on top of klinecharts. It provides a state provider, a set of hooks, and overlay templates. No UI components are included — use any UI framework you prefer.
Many features in this library — including 11 TradingView-style indicators, 9 drawing overlays, the TA math library, undo/redo, layout manager, and script editor — were ported from the QUANTIX Extended Edition fork of KLineChart-Pro by @dsavenk0. The original fork implements these as a tightly-coupled Vue 3 application; this library re-implements them as headless React hooks following the composable, framework-agnostic architecture.
- Installation
- Concept
- KlinechartsUIProvider
- Types
- Hooks
- useKlinechartsUI
- useKlinechartsUITheme
- useKlinechartsUILoading
- usePeriods
- useTimezone
- useSymbolSearch
- useIndicators
- useDrawingTools
- useKlinechartsUISettings
- useScreenshot
- useFullscreen
- useOrderLines
- useUndoRedo
- useLayoutManager
- useScriptEditor
- useWatchlist
- useCompare
- useMeasure
- useAnnotations
- useReplay
- Utilities
- Data & Constants
- Custom Indicator Templates
- Drawing Overlays
- Extensions
- State Callbacks
- Full Export List
npm install react-klinecharts-ui react-klinecharts
# or with pnpm
pnpm add react-klinecharts-ui react-klinecharts
# or with yarn
yarn add react-klinecharts-ui react-klinechartsThe library follows a headless pattern — all UI is written by the consumer. The library is responsible for:
- State management — current symbol, period, theme, indicators, timezone, screenshots
- Datafeed integration — abstract interface for loading historical data and subscribing to real-time updates
- klinecharts overlay management — indicators, drawing tools, order lines
- Utilities —
createDataLoader, overlay templates
All hooks must be called inside <KlinechartsUIProvider>.
The root provider. Wraps the application and supplies the context.
import { KlinechartsUIProvider } from "react-klinecharts-ui";
<KlinechartsUIProvider
datafeed={myDatafeed}
defaultSymbol={{ ticker: "BTCUSDT", pricePrecision: 2 }}
defaultTheme="dark"
overlays={[orderLine]}
onSymbolChange={(symbol) => saveToStorage("symbol", symbol)}
>
<App />
</KlinechartsUIProvider>;| Prop | Type | Default | Description |
|---|---|---|---|
datafeed |
Datafeed |
— | Required. Datafeed interface implementation |
defaultSymbol |
PartialSymbolInfo |
null |
Initial trading instrument |
defaultPeriod |
TerminalPeriod |
First in periods |
Initial timeframe |
defaultTheme |
string |
"light" |
Initial theme ("light" or "dark") |
defaultTimezone |
string |
"Asia/Shanghai" |
Initial timezone (IANA) |
defaultMainIndicators |
string[] |
["MA"] |
Indicators on the main chart at startup |
defaultSubIndicators |
string[] |
["VOL"] |
Indicators on sub-panels at startup |
defaultLocale |
string |
"en-US" |
Locale passed to klinecharts |
periods |
TerminalPeriod[] |
DEFAULT_PERIODS |
List of available timeframes |
styles |
DeepPartial<Styles> |
— | Custom klinecharts styles (applied when the chart is ready) |
registerExtensions |
boolean |
true |
Whether to register built-in drawing overlays |
overlays |
OverlayTemplate[] |
— | Additional overlay templates (e.g. orderLine, custom overlays) |
onStateChange |
(action, nextState, prevState) => void |
— | Called synchronously on every dispatched action |
onSymbolChange |
(symbol) => void |
— | Called when the symbol changes |
onPeriodChange |
(period) => void |
— | Called when the period changes |
onThemeChange |
(theme) => void |
— | Called when the theme changes |
onTimezoneChange |
(timezone) => void |
— | Called when the timezone changes |
onMainIndicatorsChange |
(indicators: string[]) => void |
— | Called when main indicators change |
onSubIndicatorsChange |
(indicators: Record<string, string>) => void |
— | Called when sub-indicators change |
onSettingsChange |
(settings: Record<string, unknown>) => void |
— | Called when settings change via useKlinechartsUISettings |
The provider registers overlays once on mount via useRef — passing an inline array is safe and does not cause re-registration:
// Safe — does not re-register on every render
<KlinechartsUIProvider overlays={[orderLine, myCustomOverlay]}>Data interface implemented by the consumer.
interface Datafeed {
/**
* Search symbols by a query string.
* signal — AbortSignal to cancel the request when a newer query is typed.
*/
searchSymbols(
search: string,
signal?: AbortSignal,
): Promise<PartialSymbolInfo[]>;
/**
* Load historical bars.
* from/to — timestamps in milliseconds.
* When from=0, load the most recent available data.
*/
getHistoryKLineData(
symbol: SymbolInfo,
period: TerminalPeriod,
from: number,
to: number,
): Promise<KLineData[]>;
/**
* Subscribe to real-time updates.
* callback is called for every new bar.
*/
subscribe(
symbol: SymbolInfo,
period: TerminalPeriod,
callback: (data: KLineData) => void,
): void;
/** Unsubscribe from real-time updates. */
unsubscribe(symbol: SymbolInfo, period: TerminalPeriod): void;
}Minimal description of a trading instrument.
interface PartialSymbolInfo {
ticker: string; // e.g. "BTCUSDT", "AAPL", "EUR/USD"
pricePrecision?: number; // Decimal places for price display
volumePrecision?: number; // Decimal places for volume display
[key: string]: unknown; // Any additional fields
}Complete provider state. Accessible via useKlinechartsUI().state.
interface KlinechartsUIState {
chart: Chart | null; // klinecharts Chart instance (null before onReady)
datafeed: Datafeed; // The datafeed passed to the provider
symbol: PartialSymbolInfo | null; // Current symbol
period: TerminalPeriod; // Current timeframe
theme: string; // Current theme: "light" | "dark"
timezone: string; // Current timezone (IANA)
isLoading: boolean; // true while data is loading
locale: string; // klinecharts locale ("en-US")
periods: TerminalPeriod[]; // List of available timeframes
mainIndicators: string[]; // Active main chart indicators
subIndicators: Record<string, string>; // Active sub-indicators: { name → paneId }
styles: DeepPartial<Styles> | undefined; // Custom klinecharts styles
screenshotUrl: string | null; // URL of the last screenshot
}Union type of all possible actions for dispatch.
type KlinechartsUIAction =
| { type: "SET_CHART"; chart: Chart }
| { type: "SET_SYMBOL"; symbol: PartialSymbolInfo }
| { type: "SET_PERIOD"; period: TerminalPeriod }
| { type: "SET_THEME"; theme: string }
| { type: "SET_TIMEZONE"; timezone: string }
| { type: "SET_LOADING"; isLoading: boolean }
| { type: "SET_MAIN_INDICATORS"; indicators: string[] }
| { type: "SET_SUB_INDICATORS"; indicators: Record<string, string> }
| { type: "SET_STYLES"; styles: DeepPartial<Styles> | undefined }
| { type: "SET_LOCALE"; locale: string }
| { type: "SET_SCREENSHOT_URL"; url: string | null };The primary hook — returns the full context: state, dispatch, datafeed, and fullscreen ref.
const { state, dispatch, datafeed, onSettingsChange, fullscreenContainerRef } =
useKlinechartsUI();Return value:
interface KlinechartsUIContextValue {
state: KlinechartsUIState;
dispatch: Dispatch<KlinechartsUIAction>; // enhancedDispatch with synchronous callbacks
datafeed: Datafeed;
onSettingsChange?: (settings: Record<string, unknown>) => void;
fullscreenContainerRef: RefObject<HTMLElement | null>;
}Note:
dispatchisenhancedDispatch. It synchronously computes the new state by calling the pure reducer directly and immediately invokesonStateChange/ individual callbacks — without waiting for a React re-render.
Manage the chart theme.
const { theme, setTheme, toggleTheme } = useKlinechartsUITheme();| Field | Type | Description |
|---|---|---|
theme |
string |
Current theme: "light" or "dark" |
setTheme |
(theme: string) => void |
Set a specific theme |
toggleTheme |
() => void |
Toggle between "light" and "dark" |
const { theme, toggleTheme } = useKlinechartsUITheme();
<button onClick={toggleTheme}>
{theme === "dark" ? <SunIcon /> : <MoonIcon />}
</button>;Loading state of chart data.
const { isLoading } = useKlinechartsUILoading();| Field | Type | Description |
|---|---|---|
isLoading |
boolean |
true while createDataLoader is fetching bars |
const { isLoading } = useKlinechartsUILoading();
{
isLoading && <div className="spinner" />;
}Manage timeframes.
const { periods, activePeriod, setPeriod } = usePeriods();| Field | Type | Description |
|---|---|---|
periods |
TerminalPeriod[] |
Full list of available timeframes |
activePeriod |
TerminalPeriod |
Currently selected timeframe |
setPeriod |
(period: TerminalPeriod) => void |
Change the timeframe |
TerminalPeriod extends KlinechartsPeriod with a label: string field.
Default timeframes: 1m, 5m, 15m, 1H, 2H, 4H, D, W, M, Y
const { periods, activePeriod, setPeriod } = usePeriods();
<div className="flex gap-1">
{periods.map((p) => (
<button
key={p.label}
onClick={() => setPeriod(p)}
className={activePeriod.label === p.label ? "active" : ""}
>
{p.label}
</button>
))}
</div>;Manage the chart timezone.
const { timezones, activeTimezone, setTimezone } = useTimezone();| Field | Type | Description |
|---|---|---|
timezones |
TimezoneItem[] |
Full list of timezones |
activeTimezone |
string |
Current IANA timezone key |
setTimezone |
(timezone: string) => void |
Change the timezone |
interface TimezoneItem {
key: string; // IANA: "Europe/London", "America/New_York", "UTC"
localeKey: string; // Short name: "london", "new_york", "utc"
}Available timezones:
UTC, Pacific/Honolulu, America/Juneau, America/Los_Angeles, America/Chicago, America/Toronto, America/Sao_Paulo, Europe/London, Europe/Berlin, Asia/Bahrain, Asia/Dubai, Asia/Ashkhabad, Asia/Almaty, Asia/Bangkok, Asia/Shanghai, Asia/Tokyo, Australia/Sydney, Pacific/Norfolk
const { timezones, activeTimezone, setTimezone } = useTimezone();
<select value={activeTimezone} onChange={(e) => setTimezone(e.target.value)}>
{timezones.map((tz) => (
<option key={tz.key} value={tz.key}>
{tz.key}
</option>
))}
</select>;Search and select a trading instrument.
const {
query,
results,
isSearching,
activeSymbol,
setQuery,
selectSymbol,
clearResults,
} = useSymbolSearch(debounceMs);| Parameter | Type | Default | Description |
|---|---|---|---|
debounceMs |
number |
300 |
Delay before calling datafeed.searchSymbols |
| Field | Type | Description |
|---|---|---|
query |
string |
Current search query |
results |
PartialSymbolInfo[] |
Results from the last search |
isSearching |
boolean |
true while the request is in flight |
activeSymbol |
PartialSymbolInfo | null |
Currently selected symbol from state.symbol |
setQuery |
(q: string) => void |
Update the query (triggers debounced search) |
selectSymbol |
(symbol: PartialSymbolInfo) => void |
Select a symbol — dispatches SET_SYMBOL |
clearResults |
() => void |
Clear search results |
The hook automatically cancels in-flight requests via AbortController on every new keystroke and on unmount.
const { query, results, isSearching, selectSymbol, setQuery } =
useSymbolSearch(300);
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>;
{
isSearching && <Spinner />;
}
{
results.map((sym) => (
<button key={sym.ticker} onClick={() => selectSymbol(sym)}>
{sym.ticker}
</button>
));
}Full indicator management: add/remove, visibility, parameters, move between panes.
const {
mainIndicators,
subIndicators,
activeMainIndicators,
activeSubIndicators,
availableMainIndicators,
availableSubIndicators,
addMainIndicator,
removeMainIndicator,
addSubIndicator,
removeSubIndicator,
toggleMainIndicator,
toggleSubIndicator,
moveToMain,
moveToSub,
setIndicatorVisible,
updateIndicatorParams,
getIndicatorParams,
isMainIndicatorActive,
isSubIndicatorActive,
} = useIndicators();| Field | Type | Description |
|---|---|---|
mainIndicators |
IndicatorInfo[] |
All main indicators with isActive flag |
subIndicators |
IndicatorInfo[] |
All sub-indicators with isActive flag |
activeMainIndicators |
string[] |
Active main indicator names only |
activeSubIndicators |
Record<string, string> |
Active sub-indicators: { name → paneId } |
availableMainIndicators |
string[] |
Full list from MAIN_INDICATORS |
availableSubIndicators |
string[] |
Full list from SUB_INDICATORS |
interface IndicatorInfo {
name: string; // "MA", "MACD", "RSI", etc.
isActive: boolean; // Whether it is currently on the chart
}| Method | Description |
|---|---|
addMainIndicator(name) |
Creates the indicator on candle_pane with id main_${name} |
removeMainIndicator(name) |
Removes the indicator and updates state |
addSubIndicator(name) |
Creates the indicator on a new sub-pane with id sub_${name} |
removeSubIndicator(name) |
Removes the sub-indicator and its pane |
toggleMainIndicator(name) |
add if inactive, remove if active |
toggleSubIndicator(name) |
Same for sub-indicators |
moveToMain(name) |
Moves from sub-pane to candle_pane |
moveToSub(name) |
Moves from candle_pane to a new sub-pane |
setIndicatorVisible(name, isMain, visible) |
Show/hide indicator via chart.overrideIndicator |
updateIndicatorParams(name, paneId, params) |
Update calcParams via chart.overrideIndicator |
getIndicatorParams(name) |
Returns [{ label, defaultValue }] or [] if no parameters |
isMainIndicatorActive(name) |
Quick active check |
isSubIndicatorActive(name) |
Quick active check |
Main chart (MAIN_INDICATORS): MA, EMA, SMA, BOLL, SAR, BBI
Sub-panes (SUB_INDICATORS): MA, EMA, VOL, MACD, BOLL, KDJ, RSI, BIAS, BRAR, CCI, DMI, CR, PSY, DMA, TRIX, OBV, VR, WR, MTM, EMV, SAR, SMA, ROC, PVT, BBI, AO
Indicators with configurable parameters: SMA, BOLL, SAR, BBI, MACD, KDJ, BRAR, CCI, DMI, CR, PSY, DMA, TRIX, OBV, VR, MTM, EMV, ROC, AO and others.
Manage drawing tools (chart overlays).
const {
categories,
activeTool,
magnetMode,
isLocked,
isVisible,
selectTool,
clearActiveTool,
setMagnetMode,
toggleLock,
toggleVisibility,
removeAllDrawings,
} = useDrawingTools();| Field/Method | Type | Description |
|---|---|---|
categories |
DrawingCategoryItem[] |
Tool categories with nested tools |
activeTool |
string | null |
Name of the last selected tool |
magnetMode |
"normal" | "weak" | "strong" |
Snap-to-OHLC mode |
isLocked |
boolean |
Whether all drawings are locked |
isVisible |
boolean |
Whether all drawings are visible |
selectTool(name) |
— | Start drawing via chart.createOverlay |
clearActiveTool() |
— | Deselect tool (local state only) |
setMagnetMode(mode) |
— | Change magnet mode for all existing and future drawings |
toggleLock() |
— | Toggle lock on all drawings |
toggleVisibility() |
— | Show/hide all drawings |
removeAllDrawings() |
— | Remove all drawings in the drawing_tools group |
interface DrawingToolItem {
name: string; // klinecharts overlay name, e.g. "arrow", "fibonacciLine"
localeKey: string; // Localization key
}
interface DrawingCategoryItem {
key: string; // "singleLine" | "moreLine" | "polygon" | "fibonacci" | "wave"
tools: DrawingToolItem[];
}Categories and tools:
Category (key) |
Tools |
|---|---|
singleLine |
horizontalStraightLine, horizontalRayLine, horizontalSegment, verticalStraightLine, verticalRayLine, verticalSegment, straightLine, rayLine, segment, arrow, priceLine |
moreLine |
priceChannelLine, parallelStraightLine |
polygon |
circle, rect, parallelogram, triangle |
fibonacci |
fibonacciLine, fibonacciSegment, fibonacciCircle, fibonacciSpiral, fibonacciSpeedResistanceFan, fibonacciExtension, gannBox |
wave |
xabcd, abcd, threeWaves, fiveWaves, eightWaves, anyWaves |
Manage chart appearance: candle type, colors, price marks, axes, grid, crosshair, tooltips.
const settings = useKlinechartsUISettings();| Field | Type | Default | Description |
|---|---|---|---|
candleType |
string |
"candle_solid" |
Candle display type |
candleUpColor |
string |
"#2DC08E" |
Bullish candle color |
candleDownColor |
string |
"#F92855" |
Bearish candle color |
showLastPrice |
boolean |
true |
Last price mark on Y-axis |
showHighPrice |
boolean |
true |
High price mark |
showLowPrice |
boolean |
true |
Low price mark |
showIndicatorLastValue |
boolean |
true |
Indicator last value mark |
priceAxisType |
PriceAxisType |
"normal" |
Y-axis scale type |
reverseCoordinate |
boolean |
false |
Invert Y-axis |
showTimeAxis |
boolean |
true |
Show X-axis |
showGrid |
boolean |
true |
Show grid |
showCrosshair |
boolean |
true |
Show crosshair |
showCandleTooltip |
boolean |
true |
OHLCV tooltip |
showIndicatorTooltip |
boolean |
true |
Indicator tooltip |
Candle types: candle_solid, candle_stroke, candle_up_stroke, candle_down_stroke, ohlc, area
Price axis types: "normal", "percentage", "log"
| Field | Type | Description |
|---|---|---|
candleTypes |
CandleTypeItem[] |
List of { key, localeKey } for rendering a selector |
priceAxisTypes |
{ key: PriceAxisType; localeKey: string }[] |
List for rendering a selector |
Each field has a corresponding setter: setCandleType, setCandleUpColor, setCandleDownColor, setShowLastPrice, setShowHighPrice, setShowLowPrice, setShowIndicatorLastValue, setPriceAxisType, setReverseCoordinate, setShowTimeAxis, setShowGrid, setShowCrosshair, setShowCandleTooltip, setShowIndicatorTooltip.
All setters immediately apply changes via chart.setStyles(...).
| Method | Description |
|---|---|
resetToDefaults() |
Reset all settings to defaults via chart.setStyles(theme) |
Important: Settings are stored in local
useStateinside the hook — they are not part of the provider's reducer state. The hook must be called unconditionally (not only when a dialog is open) to preserve settings across dialog open/close cycles. Changes triggeronSettingsChangefrom the provider.
Capture and download a chart screenshot.
const { screenshotUrl, capture, download, clear } = useScreenshot();| Field/Method | Type | Description |
|---|---|---|
screenshotUrl |
string | null |
JPEG data URL of the last screenshot |
capture() |
() => void |
Capture the current chart state |
download(filename?) |
(filename?: string) => void |
Download as a file (default: "chart-screenshot.jpg") |
clear() |
() => void |
Clear screenshotUrl from state |
Screenshot is created via chart.getConvertPictureUrl(true, "jpeg", bgColor). Background depends on the current theme: #151517 for dark, #ffffff for light.
const { screenshotUrl, capture, download, clear } = useScreenshot();
<button onClick={capture}>Capture</button>;
{
screenshotUrl && (
<>
<img src={screenshotUrl} alt="chart" />
<button onClick={() => download()}>Download</button>
<button onClick={clear}>Close</button>
</>
);
}Toggle fullscreen mode. Uses fullscreenContainerRef from the provider.
const { isFullscreen, toggle, enter, exit, containerRef } = useFullscreen();| Field/Method | Type | Description |
|---|---|---|
isFullscreen |
boolean |
Current fullscreen state |
toggle() |
() => void |
Toggle |
enter() |
() => void |
Enter fullscreen |
exit() |
() => void |
Exit fullscreen |
containerRef |
RefObject<HTMLElement | null> |
The same ref from the provider |
Supports cross-browser vendor prefixes (webkit, ms).
Important: containerRef must be assigned to the container element that should occupy the full screen (typically the root layout element):
const { containerRef, toggle, isFullscreen } = useFullscreen();
<div ref={containerRef as React.RefObject<HTMLDivElement>}>
<button onClick={toggle}>{isFullscreen ? "Exit" : "Fullscreen"}</button>
<ChartView />
</div>;Create and manage horizontal price level lines (order lines).
Requirement: The
orderLineoverlay must be registered viaoverlays={[orderLine]}on the provider.
const {
createOrderLine,
updateOrderLine,
removeOrderLine,
removeAllOrderLines,
} = useOrderLines();createOrderLine(options: OrderLineOptions): string | nullReturns the id of the created line, or null if the chart is not ready.
interface OrderLineOptions extends OrderLineExtendData {
id?: string; // Auto-generated if omitted
price: number; // Price level
draggable?: boolean; // Allow drag to change price. Default: false
onPriceChange?: (price: number) => void; // Called when drag ends
}All OrderLineExtendData fields (see orderLine extension) are accepted directly — they flow through as overlay extendData.
updateOrderLine(id: string, options: Partial<Omit<OrderLineOptions, "id">>): voidUpdates an existing line. Only pass the fields you want to change.
removeOrderLine(id: string): void
removeAllOrderLines(): void // Removes all overlays with name="orderLine"const { createOrderLine, updateOrderLine, removeOrderLine } = useOrderLines();
const id = createOrderLine({
price: 45000,
color: "#ff9900",
text: "Target",
line: { style: "dashed" },
mark: { bg: "#ff9900", color: "#fff" },
draggable: true,
onPriceChange: (newPrice) => console.log("Moved to:", newPrice),
});
updateOrderLine(id!, { color: "#00ff00" });
removeOrderLine(id!);Undo/redo history for drawing overlays and indicator toggles. Automatically connected to useDrawingTools and useIndicators via a shared context ref — actions are recorded without manual wiring.
Keyboard shortcuts: Ctrl+Z (undo), Ctrl+Y / Ctrl+Shift+Z (redo).
import { useUndoRedo } from "react-klinecharts-ui";
const { canUndo, canRedo, undo, redo, pushAction, clear } = useUndoRedo();| Property | Type | Description |
|---|---|---|
canUndo |
boolean |
Whether there are actions to undo |
canRedo |
boolean |
Whether there are actions to redo |
undo |
() => void |
Undo the last action |
redo |
() => void |
Redo the last undone action |
pushAction |
(action: UndoRedoAction) => void |
Push a new action onto the undo stack (clears redo) |
clear |
() => void |
Clear all undo/redo history |
| Type | Trigger | Undo behaviour | Redo behaviour |
|---|---|---|---|
overlay_added |
User completes a drawing | Removes the overlay | Re-creates the overlay |
overlays_removed |
removeAllDrawings() |
Restores all removed overlays | Re-removes them |
indicator_toggled |
Add/remove indicator | Reverses the toggle | Re-applies the toggle |
useUndoRedo registers a pushAction callback on undoRedoListenerRef (shared via provider context). When useDrawingTools finishes a drawing or useIndicators toggles an indicator, they call the ref to record the action — no prop drilling required.
Save, load, rename, and delete named chart layouts via localStorage. Captures indicators, drawings, symbol, and period. Optional auto-save with 5-second debounce.
import { useLayoutManager } from "react-klinecharts-ui";
const {
layouts,
saveLayout,
loadLayout,
deleteLayout,
renameLayout,
refreshLayouts,
autoSaveEnabled,
setAutoSaveEnabled,
} = useLayoutManager();| Property | Type | Description |
|---|---|---|
layouts |
LayoutEntry[] |
List of saved layout entries |
saveLayout |
(name: string) => string | null |
Save current chart state; returns layout ID |
loadLayout |
(id: string) => boolean |
Load and apply a layout by ID |
deleteLayout |
(id: string) => void |
Delete a layout |
renameLayout |
(id: string, name: string) => boolean |
Rename a layout |
refreshLayouts |
() => void |
Refresh the list from localStorage |
autoSaveEnabled |
boolean |
Whether auto-save is enabled |
setAutoSaveEnabled |
(enabled: boolean) => void |
Toggle auto-save |
interface LayoutEntry {
id: string;
name: string;
symbol: string;
period: string;
timestamp: number;
lastModified: number;
state: ChartLayoutState;
}interface ChartLayoutState {
version: string;
meta: { symbol: string; period: string; timestamp: number; lastModified: number };
indicators: Array<{ paneId: string; name: string; calcParams: any[]; visible: boolean }>;
drawings: Array<{ name: string; points: any[]; styles?: any; extendData?: any }>;
}Pine Script-style custom indicator editor. Users write plain JavaScript function bodies that receive TA, dataList (array of KLineData), and params (parsed from a comma-separated string). The script must return an array of objects — one per candle, each key becomes a chart series.
Scripts execute inside a sandboxed new Function() with dangerous globals shadowed: fetch, XMLHttpRequest, WebSocket, Worker, SharedWorker, importScripts, self, caches, indexedDB.
import { useScriptEditor } from "react-klinecharts-ui";
const {
code, setCode,
scriptName, setScriptName,
params, setParams,
placement, setPlacement,
error, status, isRunning, hasActiveScript,
runScript, removeScript, resetCode,
exportScript, importScript,
defaultScript,
} = useScriptEditor();| Property | Type | Description |
|---|---|---|
code |
string |
Current script source code |
setCode |
(code: string) => void |
Update the source code |
scriptName |
string |
Display name of the script |
setScriptName |
(name: string) => void |
Update the name |
params |
string |
Comma-separated numeric params (e.g. "14, 26, 9") |
setParams |
(params: string) => void |
Update params |
placement |
ScriptPlacement |
"main" or "sub" |
setPlacement |
(p: ScriptPlacement) => void |
Set placement |
error |
string |
Last error message (empty = no error) |
status |
string |
Last status message |
isRunning |
boolean |
Whether the script is currently executing |
hasActiveScript |
boolean |
Whether a script indicator is on the chart |
runScript |
() => void |
Execute the script and register the indicator |
removeScript |
() => void |
Remove the current script indicator from the chart |
resetCode |
() => void |
Reset code to the default template |
exportScript |
() => void |
Download the code as a .js file |
importScript |
(file: File) => void |
Load a script from a file |
defaultScript |
string |
The default template code |
// Available: TA, dataList, params
const period = params[0] ?? 14;
const closes = dataList.map(d => d.close);
const highs = dataList.map(d => d.high);
const lows = dataList.map(d => d.low);
const rsi = TA.rsi(closes, period);
const boll = TA.bollinger(closes, period, 2);
// Return one object per candle — each key = one line on the chart
return rsi.map((v, i) => ({
rsi: v,
upper: boll.upper[i],
mid: boll.mid[i],
lower: boll.lower[i],
}));Manage a list of tracked symbols with live price updates from your datafeed.
import { useWatchlist } from "react-klinecharts-ui";
const {
items,
addSymbol,
removeSymbol,
switchSymbol,
activeSymbol,
} = useWatchlist();| Property | Type | Description |
|---|---|---|
items |
WatchlistItem[] |
Array of tracked symbols with price data |
addSymbol |
(ticker: string) => void |
Add a symbol to the watchlist |
removeSymbol |
(ticker: string) => void |
Remove a symbol from the watchlist |
switchSymbol |
(ticker: string) => void |
Change the chart to display a symbol |
activeSymbol |
string | null |
Currently displayed symbol ticker |
| Property | Type | Description |
|---|---|---|
ticker |
string |
Symbol identifier |
lastPrice |
number | null |
Last traded price |
change |
number | null |
Absolute price change |
changePercent |
number | null |
24h percentage change |
Compare multiple symbols on the same chart with individual colors and visibility toggle.
import { useCompare } from "react-klinecharts-ui";
const {
symbols,
addSymbol,
removeSymbol,
toggleSymbol,
clearAll,
} = useCompare();| Property | Type | Description |
|---|---|---|
symbols |
CompareSymbol[] |
Array of comparison symbols with metadata |
addSymbol |
(ticker: string) => void |
Add a symbol to compare |
removeSymbol |
(ticker: string) => void |
Remove a comparison symbol |
toggleSymbol |
(ticker: string) => void |
Toggle visibility of a symbol |
clearAll |
() => void |
Remove all comparison symbols |
| Property | Type | Description |
|---|---|---|
ticker |
string |
Symbol identifier |
visible |
boolean |
Whether the symbol is currently visible on chart |
color |
string |
Hex color code for the symbol's line |
Measure price changes, percentage swings, bar count, and time intervals between two points on the chart.
import { useMeasure } from "react-klinecharts-ui";
const {
isActive,
startMeasure,
cancelMeasure,
result,
} = useMeasure();| Property | Type | Description |
|---|---|---|
isActive |
boolean |
Whether measurement mode is enabled |
startMeasure |
() => void |
Enter measurement mode (click two points) |
cancelMeasure |
() => void |
Exit measurement mode |
result |
MeasureResult | null |
Measurement data (null if not measured yet) |
| Property | Type | Description |
|---|---|---|
priceDiff |
number |
Absolute price difference |
pricePercent |
number |
Percentage change |
bars |
number |
Number of bars between points |
timeDiff |
number |
Time difference in milliseconds |
Add text annotations at specific price levels and timestamps with optional colors.
import { useAnnotations } from "react-klinecharts-ui";
const {
annotations,
addAnnotation,
removeAnnotation,
updateAnnotation,
clearAnnotations,
} = useAnnotations();| Property | Type | Description |
|---|---|---|
annotations |
Annotation[] |
Array of all annotations |
addAnnotation |
(text, price, timestamp, color?) => string |
Add annotation; returns ID |
removeAnnotation |
(id: string) => void |
Remove annotation by ID |
updateAnnotation |
(id, updates) => void |
Update text or color |
clearAnnotations |
() => void |
Remove all annotations |
| Property | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
text |
string |
Annotation text |
price |
number |
Price level |
timestamp |
number |
Candle timestamp |
color |
string | undefined |
Optional hex color |
Replay historical candles at various speeds with play/pause/step controls and progress tracking.
import { useReplay } from "react-klinecharts-ui";
const {
isPlaying,
speed,
currentIndex,
totalBars,
play,
pause,
stop,
step,
setSpeed,
} = useReplay();| Property | Type | Description |
|---|---|---|
isPlaying |
boolean |
Whether replay is running |
speed |
number |
Current playback speed (0.25, 0.5, 1, 2, 4) |
currentIndex |
number |
Current bar index being displayed |
totalBars |
number |
Total bars in the dataset |
play |
() => void |
Start replay from current position |
pause |
() => void |
Pause replay (can resume with play) |
stop |
() => void |
Stop replay and reset to start |
step |
() => void |
Advance one bar when paused |
setSpeed |
(speed: number) => void |
Set playback speed multiplier |
Creates a klinecharts DataLoader from a Datafeed instance.
function createDataLoader(
datafeed: Datafeed,
dispatch: Dispatch<KlinechartsUIAction>,
): DataLoader;This bridges the Datafeed interface to the klinecharts native DataLoader format.
Behavior:
| klinecharts request type | Action |
|---|---|
"init" |
Loads the latest ~1000 bars (to=Date.now(), from=0), saves oldestTimestamp, increments currentGen |
"forward" |
Loads bars older than oldestTimestamp - 1 for left-scroll pagination |
"backward" |
Ignored (real-time handled via subscribeBar) |
Race condition protection: each "init" increments currentGen. If a "forward" request completes after a new "init" has started, the result is discarded.
function ChartView() {
const { state, dispatch, datafeed } = useKlinechartsUI();
const dataLoader = useMemo(
() => createDataLoader(datafeed, dispatch),
[datafeed, dispatch],
);
return (
<KLineChart
dataLoader={dataLoader}
symbol={state.symbol ?? undefined}
period={state.period}
locale={state.locale}
timezone={state.timezone}
styles={state.theme}
onReady={(chart) => dispatch({ type: "SET_CHART", chart })}
/>
);
}A standalone math library for computing common technical indicators. Used internally by indicator templates and available to custom scripts via useScriptEditor.
import { TA } from "react-klinecharts-ui";| Function | Signature | Returns |
|---|---|---|
TA.sma |
(data: number[], period: number) |
number[] — Simple Moving Average |
TA.ema |
(data: number[], period: number) |
number[] — Exponential Moving Average |
TA.rma |
(data: number[], period: number) |
number[] — Running (Wilder's) Moving Average |
TA.wma |
(data: number[], period: number) |
number[] — Weighted Moving Average |
TA.hma |
(data: number[], period: number) |
number[] — Hull Moving Average |
TA.stdev |
(data: number[], period: number) |
number[] — Standard Deviation |
TA.rsi |
(data: number[], period: number) |
(number | null)[] — Relative Strength Index |
TA.macd |
(data: number[], fast?: number, slow?: number, signal?: number) |
{ dif, dea, macd } — each (number | null)[] |
TA.bollinger |
(data: number[], period?: number, mult?: number) |
{ upper, mid, lower } — each (number | null)[] |
TA.tr |
(highs: number[], lows: number[], closes: number[]) |
number[] — True Range |
TA.atr |
(highs: number[], lows: number[], closes: number[], period: number) |
number[] — Average True Range |
TA.vwap |
(highs: number[], lows: number[], closes: number[], volumes: number[]) |
number[] — Volume Weighted Average Price |
TA.cci |
(highs: number[], lows: number[], closes: number[], period: number) |
number[] — Commodity Channel Index |
TA.stoch |
(highs: number[], lows: number[], closes: number[], kPeriod?, kSmooth?, dPeriod?) |
{ k, d } — each (number | null)[] |
All constants are exported from the root index.ts:
const DEFAULT_PERIODS: TerminalPeriod[] = [
{ span: 1, type: "minute", label: "1m" },
{ span: 5, type: "minute", label: "5m" },
{ span: 15, type: "minute", label: "15m" },
{ span: 1, type: "hour", label: "1H" },
{ span: 2, type: "hour", label: "2H" },
{ span: 4, type: "hour", label: "4H" },
{ span: 1, type: "day", label: "D" },
{ span: 1, type: "week", label: "W" },
{ span: 1, type: "month", label: "M" },
{ span: 1, type: "year", label: "Y" },
];Array of TimezoneOption[] — 18 timezones. Fields: key (IANA) and localeKey (short name).
String arrays of indicator names used as the available-for-selection list.
const INDICATOR_PARAMS: Record<string, IndicatorDefinition>;
interface IndicatorDefinition {
name: string;
localeKey: string;
params: { label: string; defaultValue: number }[];
}Used internally by getIndicatorParams inside useIndicators.
Array of drawing tool categories. Used by useDrawingTools.
const CANDLE_TYPES: CandleTypeOption[] = [
{ key: "candle_solid", localeKey: "candle_solid" },
{ key: "candle_stroke", localeKey: "candle_stroke" },
{ key: "candle_up_stroke", localeKey: "candle_up_stroke" },
{ key: "candle_down_stroke", localeKey: "candle_down_stroke" },
{ key: "ohlc", localeKey: "ohlc" },
{ key: "area", localeKey: "area" },
];const PRICE_AXIS_TYPES = ["normal", "percentage", "log"] as const;
type PriceAxisType = "normal" | "percentage" | "log";11 TradingView-style indicator templates ported from QUANTIX Extended Edition. All are registered automatically via registerExtensions. Each template uses the TA library for calculations and includes custom draw functions for TradingView-like visual styling (gradient fills, dashed level lines, multi-color histograms).
import { indicators } from "react-klinecharts-ui";
// indicators === [bollTv, cci, hma, ichimoku, maRibbon, macdTv, pivotPoints, rsiTv, stochastic, superTrend, vwap]| Template | Name | Default params | Description |
|---|---|---|---|
bollTv |
BOLL_TV |
[20, 2] |
Bollinger Bands — TradingView style with filled band area, SMA midline, and upper/lower bands |
hma |
HMA |
[9] |
Hull Moving Average — high smoothing with minimal lag via TA.hma() |
ichimoku |
ICHIMOKU |
[9, 26, 52, 26] |
Ichimoku Cloud — Tenkan-sen, Kijun-sen, Senkou Span A/B (filled cloud), Chikou Span |
maRibbon |
MA_RIBBON |
[5, 10, 20, 30, 50, 100] |
Moving Average Ribbon — 6 EMAs with distinct colors for trend visualization |
pivotPoints |
PIVOT_POINTS |
[1] |
Standard pivot with Pivot, R1, R2, S1, S2 levels as dashed horizontal lines |
superTrend |
SUPERTREND |
[10, 3] |
ATR-based trend — dynamic green (up) / red (down) line coloring |
vwap |
VWAP |
[] |
Volume Weighted Average Price — single blue line via TA.vwap() |
| Template | Name | Default params | Description |
|---|---|---|---|
macdTv |
MACD_TV |
[12, 26, 9] |
4-color histogram: growing-positive (#26A69A), shrinking-positive (#B2DFDB), growing-negative (#FFCDD2), shrinking-negative (#EF5350). MACD line (#2962FF), Signal line (#FF6D00) |
rsiTv |
RSI_TV |
[14, 14] |
RSI + MA line. Dashed levels at 70/50/30. Gradient fills in overbought (>70, red) and oversold (<30, green) zones |
cci |
CCI |
[20] |
Commodity Channel Index via TA.cci() with dashed +100/0/-100 reference lines |
stochastic |
STOCHASTIC |
[14, 1, 3] |
%K (#2962FF) and %D (#FF6D00) lines. Dashed 80/50/20 levels. Gradient fills in overbought/oversold zones |
OverlayTemplate instances for drawing tools. Automatically registered when registerExtensions: true (default).
Named exports:
| Export | Overlay name | Description |
|---|---|---|
arrow |
"arrow" |
Arrow |
circle |
"circle" |
Circle |
rect |
"rect" |
Rectangle |
triangle |
"triangle" |
Triangle |
parallelogram |
"parallelogram" |
Parallelogram |
fibonacciCircle |
"fibonacciCircle" |
Fibonacci circle |
fibonacciSegment |
"fibonacciSegment" |
Fibonacci segment |
fibonacciSpiral |
"fibonacciSpiral" |
Fibonacci spiral |
fibonacciSpeedResistanceFan |
"fibonacciSpeedResistanceFan" |
Fibonacci fan |
fibonacciExtension |
"fibonacciExtension" |
Fibonacci extension |
gannBox |
"gannBox" |
Gann box |
threeWaves |
"threeWaves" |
3-wave pattern |
fiveWaves |
"fiveWaves" |
5-wave pattern |
eightWaves |
"eightWaves" |
8-wave pattern |
anyWaves |
"anyWaves" |
Custom wave pattern |
abcd |
"abcd" |
ABCD pattern |
xabcd |
"xabcd" |
XABCD pattern |
elliottWave |
"elliottWave" |
Elliott Wave (5-wave cycle with numbered vertices) |
gannFan |
"gannFan" |
Gann Fan (1x1, 1x2, etc.) |
fibRetracement |
"fibRetracement" |
Fibonacci retracement levels |
parallelChannel |
"parallelChannel" |
Parallel channel (two parallel lines) |
longPosition |
"longPosition" |
Long position risk/reward (TP/SL with % labels) |
shortPosition |
"shortPosition" |
Short position risk/reward (TP/SL with % labels) |
measure |
"measure" |
Measure tool (price %, bar count, time delta) |
brush |
"brush" |
Freehand brush drawing (Bezier smoothing) |
ray |
"ray" |
Infinite ray from a point |
Registers all built-in drawing overlays via registerOverlay. Called automatically by the provider when registerExtensions: true.
import { registerExtensions } from "react-klinecharts-ui";
registerExtensions(); // Idempotent — repeated calls are ignoredOverlay template for horizontal price level lines. Not registered automatically — must be passed explicitly to the provider.
import { orderLine, KlinechartsUIProvider } from "react-klinecharts-ui";
<KlinechartsUIProvider overlays={[orderLine]}>...</KlinechartsUIProvider>;Implementation details:
needDefaultYAxisFigure: false— custom rendering of the Y-axis markcreateYAxisFigures— draws a colored price mark on the right axiscreatePointFigures— draws a horizontal line with an optional text labelperformEventPressedMove— updatespoints[0].valueduring drag- Uses
chart.getSymbol()?.pricePrecision ?? 2for price formatting
All fields are optional. Defaults produce an orange dashed line with a white-on-orange price mark.
interface OrderLineExtendData {
/** Primary color for line, mark bg, and label fallback. Default: "rgba(255, 165, 0, 0.85)" */
color?: string;
/** Optional text label displayed above the line. */
text?: string;
/** Line style overrides. */
line?: OrderLineLineStyle;
/** Y-axis price mark style overrides. */
mark?: OrderLineMarkStyle;
/** Text label style overrides. */
label?: OrderLineLabelStyle;
}
interface OrderLineLineStyle {
style?: "solid" | "dashed" | "dotted"; // Default: "dashed"
width?: number; // Default: 1
dashedValue?: [number, number]; // Default: [4, 2]
}
interface OrderLineMarkStyle {
color?: string; // Text color. Default: "#ffffff"
bg?: string; // Background (falls back to top-level color)
borderRadius?: number; // Default: 2
font?: OrderLineFontStyle;
padding?: OrderLinePadding;
}
interface OrderLineLabelStyle {
color?: string; // Text color (falls back to top-level color)
bg?: string; // Background. Default: "transparent"
borderRadius?: number; // Default: 0
font?: OrderLineFontStyle;
padding?: OrderLinePadding;
offset?: OrderLinePadding; // Position offset. Defaults: x=8, y=3
}
interface OrderLineFontStyle {
size?: number; // Default: 11
family?: string; // Default: "Helvetica Neue, Arial, sans-serif"
weight?: string; // Default: "bold" (mark) / "normal" (label)
}
interface OrderLinePadding {
x?: number;
y?: number;
}All sub-interfaces (OrderLineLineStyle, OrderLineMarkStyle, OrderLineLabelStyle, OrderLineFontStyle, OrderLinePadding) are exported as named types from both the main and extensions entry points.
Overlay template for visualizing order book depth as horizontal liquidity bars at each price level. Shows cumulative buy/sell volume at price levels, useful for understanding support/resistance zones.
import { depthOverlay, KlinechartsUIProvider } from "react-klinecharts-ui/extensions";
<KlinechartsUIProvider overlays={[depthOverlay]}>...</KlinechartsUIProvider>;Implementation details:
needDefaultPointFigure: false,needDefaultXAxisFigure: false,needDefaultYAxisFigure: false— entirely custom renderingcreatePointFigures— draws rect figures at each price level usingyAxis.convertToPixel(price)for positioningzLevel: -1— renders behind the main candlestick data- Dynamic data via
extendData— update withchart.overrideOverlay({ id, extendData: newData })
interface DepthOverlayExtendData {
rows?: DepthOverlayRow[];
/** Optional: customize buy side color. Default: "rgba(16, 186, 156, 0.3)" */
buyColor?: string;
/** Optional: customize sell side color. Default: "rgba(239, 68, 68, 0.3)" */
sellColor?: string;
}
interface DepthOverlayRow {
price: number;
buyVolume: number;
sellVolume: number;
}Usage example:
const chart = state.chart;
if (chart) {
const rows: DepthOverlayRow[] = [
{ price: 40000, buyVolume: 5.2, sellVolume: 3.1 },
{ price: 40100, buyVolume: 4.8, sellVolume: 4.2 },
// ...
];
chart.overrideOverlay({
id: "depthOverlay",
extendData: { rows, buyColor: "rgba(34, 197, 94, 0.3)" },
});
}Array of all built-in drawing overlays — for direct access to the list:
import { overlays } from "react-klinecharts-ui";
// overlays === [arrow, circle, rect, triangle, ...] (26 items)Callbacks are invoked synchronously inside enhancedDispatch, before the React re-render — enabling immediate state persistence.
<KlinechartsUIProvider
datafeed={datafeed}
onStateChange={(action, nextState, prevState) => {
// Called for every dispatched action
console.log(action.type, nextState);
}}
onSymbolChange={(symbol) => {
localStorage.setItem('symbol', JSON.stringify(symbol));
}}
onPeriodChange={(period) => {
localStorage.setItem('period', JSON.stringify(period));
}}
onThemeChange={(theme) => {
localStorage.setItem('theme', theme);
}}
onTimezoneChange={(timezone) => {
localStorage.setItem('timezone', timezone);
}}
onMainIndicatorsChange={(indicators) => {
localStorage.setItem('mainIndicators', JSON.stringify(indicators));
}}
onSubIndicatorsChange={(indicators) => {
localStorage.setItem('subIndicators', JSON.stringify(indicators));
}}
onSettingsChange={(settings) => {
// Called from useKlinechartsUISettings on every change
localStorage.setItem('settings', JSON.stringify(settings));
}}
>Restoring persisted state:
const savedSymbol = JSON.parse(localStorage.getItem('symbol') ?? 'null');
const savedTheme = localStorage.getItem('theme') ?? 'dark';
<KlinechartsUIProvider
defaultSymbol={savedSymbol ?? { ticker: 'BTCUSDT' }}
defaultTheme={savedTheme}
>// Provider & context
export { KlinechartsUIProvider };
export {
useKlinechartsUI,
KlinechartsUIStateContext,
KlinechartsUIDispatchContext,
};
export type {
KlinechartsUIOptions,
KlinechartsUIState,
KlinechartsUIAction,
KlinechartsUIContextValue,
KlinechartsUIDispatchValue,
Datafeed,
PartialSymbolInfo,
};
// Hooks
export { useKlinechartsUITheme, type UseKlinechartsUIThemeReturn };
export { useKlinechartsUILoading, type UseKlinechartsUILoadingReturn };
export { usePeriods, type UsePeriodsReturn };
export { useTimezone, type UseTimezoneReturn, type TimezoneItem };
export { useSymbolSearch, type UseSymbolSearchReturn };
export { useIndicators, type UseIndicatorsReturn, type IndicatorInfo };
export {
useDrawingTools,
type UseDrawingToolsReturn,
type DrawingToolItem,
type DrawingCategoryItem,
};
export {
useKlinechartsUISettings,
type UseKlinechartsUISettingsReturn,
type KlinechartsUISettingsState,
type CandleTypeItem,
};
export { useScreenshot, type UseScreenshotReturn };
export { useFullscreen, type UseFullscreenReturn };
export { useOrderLines, type UseOrderLinesReturn, type OrderLineOptions };
export {
useUndoRedo,
type UseUndoRedoReturn,
type UndoRedoAction,
type UndoRedoActionType,
};
export {
useLayoutManager,
type UseLayoutManagerReturn,
type LayoutEntry,
type ChartLayoutState,
};
export {
useScriptEditor,
type UseScriptEditorReturn,
type ScriptPlacement,
};
// Data
export { DEFAULT_PERIODS, type TerminalPeriod };
export { TIMEZONES, type TimezoneOption };
export {
MAIN_INDICATORS,
SUB_INDICATORS,
INDICATOR_PARAMS,
type IndicatorParamConfig,
type IndicatorDefinition,
};
export {
DRAWING_CATEGORIES,
type DrawingTool,
type DrawingToolCategory,
type MagnetMode,
};
export {
CANDLE_TYPES,
PRICE_AXIS_TYPES,
YAXIS_POSITIONS,
COMPARE_RULES,
TOOLTIP_SHOW_RULES,
type CandleTypeOption,
type PriceAxisType,
type YAxisPosition,
type CompareRule,
type TooltipShowRule,
};
// Utilities
export { createDataLoader };
export { default as TA } from "./utils/TA";
// Drawing overlays (all 26)
export {
arrow, circle, rect, triangle, parallelogram,
fibonacciCircle, fibonacciSegment, fibonacciSpiral,
fibonacciSpeedResistanceFan, fibonacciExtension,
fibRetracement,
gannBox, gannFan,
threeWaves, fiveWaves, eightWaves, anyWaves, elliottWave,
abcd, xabcd,
parallelChannel, ray,
longPosition, shortPosition,
measure, brush,
};
// Custom indicator templates (all 11)
export * from "./indicators";
// Extensions
export { registerExtensions, overlays, indicators, orderLine };
export type {
OrderLineExtendData,
OrderLineLineStyle,
OrderLineMarkStyle,
OrderLineLabelStyle,
OrderLineFontStyle,
OrderLinePadding,
};