Get started
Step 1 — Get TradingView Charting Library access
@iccandle/reactjs-widget requires TradingView Advanced Charts (Charting Library). Request access, install the library, and host it as static files by following TradingView’s Get started guide.
Serve the build from a path your app can load (e.g. public/charting_library/ in Vite or Create React App). The library is not bundled inside @iccandle/reactjs-widget; it loads at runtime via library_path on the widget options.
Step 2 — Install the package
npm install @iccandle/reactjs-widget
# or
pnpm add @iccandle/reactjs-widget
Ensure react and react-dom are installed and meet the peer version range (React 18+).
Step 3 — Bootstrap TradingView with replay helpers
Two helpers are required for chart replay / predict from the results iframe:
| Helper | Where | Purpose |
|---|---|---|
withPlayChart | datafeed | Wraps subscribeBars so the widget can inject replay bars and block live ticks during playback. |
getCustomIndicators | custom_indicators_getter | Registers the “Generated Candles” studies that draw predicted OHLC overlays. |
Pass the same theme ("light" or "dark") to getCustomIndicators as your chart uses. See Configuration for signatures and study names.
import { useEffect, useRef, useState } from "react";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import { withPlayChart, getCustomIndicators } from "@iccandle/reactjs-widget";
const LIBRARY_PATH = "/charting_library/";
const containerRef = useRef<HTMLDivElement>(null);
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
null,
);
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: withPlayChart(yourDatafeed),
locale: "en",
autosize: true,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
custom_indicators_getter: () => getCustomIndicators("light"),
};
const tv = new widget(options);
setChartWidget(tv);
return () => {
try {
tv.remove();
} catch {
/* no-op */
}
setChartWidget(null);
};
}, []);
You must supply a valid datafeed, symbol, interval, locale, and any other options required by your TradingView license and app. Import paths for widget and types differ by setup — follow TradingView’s docs for your bundler.
If your integration only exposes the instance after onChartReady, call setChartWidget inside that callback instead of immediately after new widget(...).
Step 4 — Wrap the chart with WidgetIccandle
WidgetIccandle wraps your chart and renders the scanner overlay plus the results iframe. Pass the live widget instance (or null while mounting):
import { WidgetIccandle } from "@iccandle/reactjs-widget";
<WidgetIccandle chartWidget={chartWidget} theme="light" language="en">
<div ref={containerRef} style={{ height: "100%", minHeight: 400 }} />
</WidgetIccandle>
Render prop for chart refs
Use a render-prop children when you need chartRefs for generated-candle studies (replay / predict):
<WidgetIccandle chartWidget={chartWidget} theme="dark" language="en">
{(chartRefs) => (
<ChartHost containerRef={containerRef} chartRefs={chartRefs} onReady={setChartWidget} />
)}
</WidgetIccandle>
A plain React node also works if you do not need those refs.
Step 5 — Enable Date Range drawing
The scanner selects a bar window with TradingView’s Date Range tool. Enable it in drawings_access:
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
Default window size is 25 bars.
Auth
Users sign in through the embedded results app. On success, the embed posts sign-in-success and the parent stores iccandle_token in localStorage. Scan cache and pattern tracker APIs use that token as a Bearer credential.
You do not pass an API key prop to WidgetIccandle — auth is handled via the iframe login flow.
Theme and language
theme="light"/theme="dark"— forces that palette for scanner chrome and the iframe.theme="system"— followsprefers-color-scheme.language— one ofen,zh,vi,th,ko,ja,mn,ru(defaults toen). Applied to scanner UI and the results iframe locale.
Full working example
import { useEffect, useRef, useState } from "react";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import {
WidgetIccandle,
withPlayChart,
getCustomIndicators,
} from "@iccandle/reactjs-widget";
const LIBRARY_PATH = "/charting_library/";
export function ChartWithIccandle() {
const containerRef = useRef<HTMLDivElement>(null);
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
null,
);
return (
<div style={{ height: "100vh", width: "100%" }}>
<WidgetIccandle
chartWidget={chartWidget}
theme="light"
language="en"
onCloseResult={() => {
/* optional: react when the embed posts close-result */
}}
>
{(chartRefs) => (
<ChartHost
containerRef={containerRef}
chartRefs={chartRefs}
onReady={setChartWidget}
/>
)}
</WidgetIccandle>
</div>
);
}
function ChartHost({
containerRef,
chartRefs,
onReady,
}: {
containerRef: React.RefObject<HTMLDivElement | null>;
chartRefs: import("@iccandle/reactjs-widget").WidgetIccandleChartRefs;
onReady: (w: IChartingLibraryWidget | null) => void;
}) {
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: withPlayChart(yourDatafeed),
locale: "en",
autosize: true,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
custom_indicators_getter: () => getCustomIndicators("light"),
};
const tv = new widget(options);
onReady(tv);
return () => {
try {
tv.remove();
} catch {
/* no-op */
}
onReady(null);
};
}, [containerRef, chartRefs, onReady]);
return <div ref={containerRef} style={{ height: "100%", width: "100%" }} />;
}
Replace yourDatafeed and widget options with your real datafeed and TradingView settings.
Features overview
Pattern scanner
- Subscribes to chart readiness, symbol, resolution, and drawing events.
- Manages a
date_rangemultipoint drawing for the bar window. - On scan: posts candles to the cache service, then navigates the results iframe with search parameters.
- Optional advanced filters (symbols, top-k, lookback period, probability window) stored in
localStorageundersearch-filter.
Results iframe
- Loads
https://embed-iccandle-app.iccandle.ai/{language}?theme=...by default. - After scan / news navigation, the
srcupdates with search or similar-events paths. - On iframe load, the parent posts
{ type: "parent-origin", origin }so Stripe checkout can return to the host domain.
Pattern tracker
From the scanner popup, users can open a subscription modal to track a custom pattern (name + timeframes + symbols). Requires a valid iccandle_token.
News / economic events
- Clicking a timescale mark can open an event info modal (currency calendar events; skips holidays / early / sentiment-only marks).
- “Go to detail” loads similar events in the iframe and draws a vertical line on the chart.
- Marks can be driven from
localStoragekeystv:selected-news-eventsandtv:clicked-news-eventif your datafeed implementsgetTimescaleMarks— see Optional: timescale marks.
Common patterns
| Pattern | Approach |
|---|---|
| Full-page chart + results | Parent with height: 100vh; WidgetIccandle fills the viewport and lays out chart + iframe. |
| Theme sync | Pass theme from your app shell (light / dark / system). |
| Locale sync | Pass language to match your app locale. |
| Gate scans until iframe ready | Pass iframeLoaded={true} once the results iframe has loaded (or omit the prop if you do not gate). |
| News marks on the time axis | Implement getTimescaleMarks and sync with localStorage — see Configuration. |