Skip to main content

Get started

Step 1 - Install the package​

npm install @iccandle/selector

Ensure react and react-dom are installed and meet the peer version range.

Step 2 - Host the Charting Library​

Copy the TradingView Charting Library build into a path your app can serve as static files (e.g. public/charting_library/ in Vite or Create React App).

Import the library constructor from that path in your bundler setup (see TradingView’s integration docs for your framework). The library is not bundled inside @iccandle/selector; it loads at runtime via library_path (or equivalent) on the widget options.

Step 3 - Bootstrap TradingView and capture the widget instance​

Create a ref for the chart DOM node, instantiate the widget in useEffect, store the instance in React state, and remove it on cleanup:

import { useEffect, useRef, useState } from "react";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";

const LIBRARY_PATH = "/charting_library/"; // must match your hosted assets

// Inside your component:
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: yourDatafeed,
locale: "en",
autosize: true,
};

const tv = new widget(options);
setChartWidget(tv);

return () => {
try {
tv.remove();
} catch {
/* no-op */
}
setChartWidget(null);
};
},
[
/* library_path, datafeed identity, or other inputs that should recreate the chart */
],
);

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 (charting_library/charting_library, ./public/charting_library, etc.-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 SelectorWidget​

SelectorWidget must wrap the same subtree that contains the chart container so the scanner overlay positions correctly. Pass the live widget instance (or null while mounting):

import { SelectorWidget } from "@iccandle/selector";

const widgetKey = "icc_search_..."; // your iC Candle key

<SelectorWidget
chartWidget={chartWidget}
widgetKey={widgetKey}
theme="system"
submitCallback={(iframeSrc) => {
/* see Step 5 */
}}
>
<div ref={containerRef} style={{ height: "100%", minHeight: 400 }} />
</SelectorWidget>;

Step 5 - Handle the plugin iframe URL​

After a successful scan setup, submitCallback receives a full HTTPS URL for the iC Candle plugin iframe. The query string typically includes the bar window (timestamps / size), symbol, candle_id, apiKey (your widget key), resolved theme, and any active filters-use it as-is in an iframe src or deep link.

Open in a new tab​

submitCallback={(iframeSrc) => {
window.open(iframeSrc, "_blank", "noopener,noreferrer");
}}

Show in a modal or side panel​

Store the URL in state and render:

{
iframeSrc ? (
<iframe title="iC Candle pattern search" src={iframeSrc} className="..." />
) : null;
}

TypeScript: import SelectorWidgetProps if you wrap SelectorWidget in your own component. Import IChartingLibraryWidget from your Charting Library typings path - this package does not re-export TradingView types.

Theme and remote branding​

  • theme="light" / theme="dark" - forces that palette for the scanner chrome and for values forwarded into the plugin URL.
  • theme="system" (recommended when you don’t control parent theme) - follows prefers-color-scheme for light/dark resolution.

On mount, the widget fetches your org’s tokens from iC Candle and sets CSS custom properties on the widget root (e.g. --iccandle-primary, --iccandle-border). The same resolved theme is reflected in the iframe URL so the plugin UI stays consistent.

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 { SelectorWidget } from "@iccandle/selector";

const LIBRARY_PATH = "/charting_library/";
const WIDGET_KEY = "icc_search_your48hexcharactershere............";

export function ChartWithIccandleScanner() {
const containerRef = useRef<HTMLDivElement>(null);
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
null,
);
const [pluginSrc, setPluginSrc] = useState("");

useEffect(() => {
const el = containerRef.current;
if (!el) return;

const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: yourDatafeed,
locale: "en",
autosize: true,
fullscreen: false,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
};

const tv = new widget(options);
setChartWidget(tv);

return () => {
try {
tv.remove();
} catch {
/* no-op */
}
setChartWidget(null);
};
}, []);

return (
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<div style={{ height: 500, width: "100%" }}>
<SelectorWidget
chartWidget={chartWidget}
widgetKey={WIDGET_KEY}
theme="system"
submitCallback={(iframeSrc) => setPluginSrc(iframeSrc)}
>
<div ref={containerRef} style={{ height: "100%", width: "100%" }} />
</SelectorWidget>
</div>
{pluginSrc ? (
<iframe
title="iC Candle search"
src={pluginSrc}
style={{ width: "100%", height: 600, border: "1px solid #ccc" }}
/>
) : null}
</div>
);
}

Replace yourDatafeed and widget options with your real datafeed and TradingView settings. For a concrete in-repo reference (custom datafeed, timezone, visibility handling)

Common patterns​

PatternApproach
Modal iframeOn submitCallback, set state and render <iframe src={url} /> inside a <dialog>, Radix Dialog, MUI Modal, etc.
New tabwindow.open(iframeSrc, "_blank", "noopener,noreferrer").
Split layoutKeep the chart in one column and mount the iframe in another when pluginSrc is set (as in the example above).
News / events on the time axisIf your datafeed implements getTimescaleMarks, sync marks with localStorage - see Optional: timescale marks.