BloxwapChart SDK
Guides

React integration

Render a chart with one component, and reach the chart instance when you need it.

@bloxwap/chart/react exports a <Chart> component. React is an optional peer dependency, so the core package stays dependency-free and only this import needs React.

A chart in one component

import { Chart } from '@bloxwap/chart/react';
import type { Candle } from '@bloxwap/chart';

export function PriceChart({ data }: { data: Candle[] }) {
  return <Chart data={data} height={360} />;
}

The component is marked 'use client', so it works in the Next.js App Router. It:

  • creates the chart after mount and fills its box, following size and device pixel ratio changes;
  • calls setData when data changes, without recreating the chart;
  • follows the system color scheme by default, switching live when it changes, and applies theme changes live;
  • keeps a bar-close countdown live: while the config shows one (statusLine.countdown or priceAxis.labels.countdown, including one turned on later with updateConfig or the settings card), it runs startCountdownTicker on the page's timers;
  • destroys the chart on unmount, which also makes it safe under React Strict Mode.
PropTypeMeaning
datareadonly Candle[]Candles to display; a new array replaces the data
theme'system' | 'dark' | 'light'Color theme; default 'system', which follows prefers-color-scheme. With a preset and no theme, none is applied
preset'bloxwapDark' | DeepPartial<ChartConfig>Brand preset, read once at mount (preset < theme < config)
configDeepPartial<ChartConfig>Initial configuration, applied once at mount
now() => numberClock for the bar-close countdown, read once at mount; default () => Date.now()
countdownTickerbooleanRepaint a configured countdown every second, read once at mount; default true. Pass false if you run your own ticker
heightnumber | stringHeight of the chart box; default 400. The width fills the parent
className, stylePassed to the chart box
onReady(chart) => voidCalled once with the chart instance

Reach the chart instance

Pass a ref to stream updates, add drawings, or attach the toolbar. onReady runs once, which suits one-time setup like indicators:

import { useEffect, useRef } from 'react';
import { Chart, type ChartInstance } from '@bloxwap/chart/react';

export function LiveChart({ data, feed }) {
  const chart = useRef<ChartInstance | null>(null);

  // Stream live candles into the current bar.
  useEffect(() => feed.subscribe((candle) => chart.current?.appendData(candle)), [feed]);

  return (
    <Chart
      ref={chart}
      data={data}
      theme="dark"
      onReady={(c) => c.addIndicator({ name: 'sma', params: { period: 20 } })}
    />
  );
}

config, preset, and now are read once at mount; change configuration later through the instance with updateConfig. Attach the toolbar and settings from an effect like the one above, and dispose of their handles in its cleanup.

Presets

<Chart data={data} preset="bloxwapDark" />                 // keeps the preset's colors
<Chart data={data} preset="bloxwapDark" theme="light" />   // preset < theme < config
<Chart data={data} theme="dark" now={() => serverNow()} /> // clock for the bar-close countdown
<Chart data={data} config={{ statusLine: { visible: true, countdown: true } }} /> // a live countdown, no ticker code

With a preset and no theme prop, <Chart> doesn't follow the system color scheme, because a built-in theme would replace the preset's colors. Pass theme="system" to follow it anyway. See theming and presets.

With a paging datafeed

The paging datafeed attaches to the instance. Every new data array replaces the chart's data, so give <Chart> a stable empty array and let the datafeed own the data:

import { useEffect, useState } from 'react';
import { Chart, type ChartInstance } from '@bloxwap/chart/react';
import { attachDatafeed, type Datafeed } from '@bloxwap/chart/datafeed';
import type { Candle } from '@bloxwap/chart';

const NO_DATA: Candle[] = [];

export function MarketChart({ symbol }: { symbol: string }) {
  const [chart, setChart] = useState<ChartInstance | null>(null);
  const [datafeed, setDatafeed] = useState<Datafeed | null>(null);

  useEffect(() => {
    if (chart === null) return;
    const feed = attachDatafeed(chart, { fetchBars });
    setDatafeed(feed);
    return () => feed.destroy();
  }, [chart]);

  useEffect(() => {
    void datafeed?.setSymbol(symbol, datafeed.intervalMs ?? 15 * 60_000);
    return priceFeed.subscribe(symbol, (mid) => datafeed?.pushTick(mid, symbol));
  }, [datafeed, symbol]);

  return <Chart data={NO_DATA} preset="bloxwapDark" onReady={setChart} />;
}

For a complete bloxwap.pro component with the header, drawing toolbar, and indicators, see migrating from TradingView.

Without the component

If you prefer to own the lifecycle, the core does the sizing for you with autoResize:

'use client';

import { useEffect, useRef } from 'react';
import { createChart, type Candle } from '@bloxwap/chart';

export function PriceChart({ data }: { data: Candle[] }) {
  const canvas = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const chart = createChart({ container: canvas.current!, autoResize: true, theme: 'dark', config: { data } });
    return () => chart.destroy();
  }, [data]);

  return <div style={{ height: 400 }}><canvas ref={canvas} /></div>;
}

autoResize places the canvas out of flow to fill its parent, so give the parent a height. Because the canvas never contributes to the size being measured, there is no resize feedback loop.

Edit on GitHub

On this page