1 GitHub star = 1 new component.

Star the repo
Charts & Statschartsvgzero-dependencycandlestickohlcfinancetrading

Candlestick Chart

OHLC price chart with hollow up-candles and filled down-candles so direction never relies on color.

Live Preview

ACME / USD

Daily candles

$196.19+7.80%
  • Up (hollow)
  • Down (filled)
$175.00$180.00$185.00$190.00$195.00$200.00Oct 6Oct 10Oct 14Oct 18Oct 22Oct 26
View data
DateOpenHighLowClose
Oct 6$182.00$183.00$178.60$182.60
Oct 7$182.60$184.45$180.16$183.09
Oct 8$183.09$187.52$181.50$185.31
Oct 9$185.31$189.72$184.39$186.84
Oct 10$186.84$190.13$184.13$184.64
Oct 11$184.64$188.04$182.49$182.89
Oct 12$182.89$189.90$182.28$186.71
Oct 13$186.71$193.95$185.60$191.26
Oct 14$191.26$193.21$187.23$189.08
Oct 15$189.08$190.13$181.66$184.41
Oct 16$184.41$187.36$181.33$186.64
Oct 17$186.64$194.82$184.49$193.17
Oct 18$193.17$196.11$191.83$193.65
Oct 19$193.65$196.70$187.75$188.50
Oct 20$188.50$191.86$187.36$187.80
Oct 21$187.80$196.52$187.36$193.16
Oct 22$193.16$199.02$192.42$195.96
Oct 23$195.96$198.43$191.97$193.30
Oct 24$193.30$194.97$189.47$191.60
Oct 25$191.60$194.60$188.54$193.86
Oct 26$193.86$197.04$191.09$196.01
Oct 27$196.01$198.13$194.15$196.19

Usage

Copy and paste this code into your React component:

tsx
import { CandlestickChart } from 'react-component-kit-ui/charts';

<CandlestickChart title="ACME / USD" candles={[
    { label: 'Oct 1', open: 182, high: 186, low: 180, close: 185 },
    { label: 'Oct 2', open: 185, high: 187, low: 181, close: 182 },
  ]} />

Manual Installation

If you are not using the npm package, create a new file and paste the following code: src/components/CandlestickChart.tsx + CandlestickChart.css:

CandlestickChart.tsx
'use client';

import ChartFrame from './ChartFrame';
import { scaleLinear, niceTicks, POSITIVE, NEGATIVE } from './chart-utils';

interface Candle { label: string; open: number; high: number; low: number; close: number }

const demo: Candle[] = (() => {
  let p = 182;
  return Array.from({ length: 22 }, (_, i) => {
    const r1 = Math.sin(i * 4.7) * 0.5 + Math.sin(i * 1.3) * 0.5;
    const r2 = Math.abs(Math.sin(i * 9.1));
    const open = p;
    const close = Math.round((p + r1 * 6 + 0.6) * 100) / 100;
    const high = Math.round((Math.max(open, close) + r2 * 3 + 0.4) * 100) / 100;
    const low = Math.round((Math.min(open, close) - (1 - r2) * 3 - 0.4) * 100) / 100;
    p = close;
    return { label: `Oct ${i + 6}`, open, high, low, close };
  });
})();

/**
 * CandlestickChart — OHLC price chart. Bodies span open→close, wicks span
 * low→high. Up candles are hollow, down candles filled, so direction is
 * readable without color.
 *
 * @param {Array<{label: string, open: number, high: number, low: number, close: number}>} candles - OHLC data.
 * @param {string} prefix - Price prefix.
 * @param {number} height - SVG height in viewBox units (width is 640).
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
export interface CandlestickChartProps {
  title?: string;
  subtitle?: string;
  candles?: Candle[];
  prefix?: string;
  height?: number;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function CandlestickChart({
  title = 'ACME / USD',
  subtitle = 'Daily candles',
  candles = demo,
  prefix = '$',
  height = 300,
  variant = 'glass',
  size = 'md',
  className = '',
}: CandlestickChartProps) {
  const W = 640;
  const H = height;
  const m = { t: 12, r: 52, b: 28, l: 12 };
  const ticks = niceTicks(Math.min(...candles.map((c) => c.low)), Math.max(...candles.map((c) => c.high)), 5);
  const y = scaleLinear(ticks[0], ticks[ticks.length - 1], H - m.b, m.t);
  const band = (W - m.l - m.r) / candles.length;
  const bw = Math.max(3, band * 0.6);
  const f = (v: number) => `${prefix}${v.toFixed(2)}`;
  const last = candles[candles.length - 1];
  const first = candles[0];
  const change = ((last.close - first.open) / first.open) * 100;
  const labelEvery = Math.ceil(candles.length / 6);
  return (
    <>
      <ChartFrame title={title} subtitle={subtitle} value={f(last.close)} delta={`${change >= 0 ? '+' : ''}${change.toFixed(2)}%`} deltaPositive={change >= 0} variant={variant} size={size} className={className} legend={[{ label: 'Up (hollow)', color: POSITIVE }, { label: 'Down (filled)', color: NEGATIVE }]} table={{ headers: ['Date', 'Open', 'High', 'Low', 'Close'], rows: candles.map((c) => [c.label, f(c.open), f(c.high), f(c.low), f(c.close)]) }}>
        <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: candlestick chart, ${candles.length} periods, ${change.toFixed(1)}% change`}>
          {ticks.map((t, __i: number) => (
            <g key={__i}>
              <line className="cf-grid" x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} />
              <text className="cf-label" x={W - m.r + 8} y={y(t) + 4}>{f(t)}</text>
            </g>
          ))}
          {candles.map((c, i) => {
            const cx = m.l + i * band + band / 2;
            const up = c.close >= c.open;
            const col = up ? POSITIVE : NEGATIVE;
            const top = y(Math.max(c.open, c.close));
            const h = Math.max(1.5, Math.abs(y(c.open) - y(c.close)));
            return (
              <g className="cf-mark cf-dim" tabIndex={0} role="img" aria-label={`${c.label}: open ${f(c.open)}, high ${f(c.high)}, low ${f(c.low)}, close ${f(c.close)}`} data-tip-title={c.label} data-tip={`${col}::${up ? 'Up' : 'Down'} ${(((c.close - c.open) / c.open) * 100).toFixed(2)}%|O ${f(c.open)} · H ${f(c.high)}|L ${f(c.low)} · C ${f(c.close)}`}>
                <rect x={cx - band / 2} y={m.t} width={band} height={H - m.t - m.b} fill="transparent" />
                <line x1={cx} x2={cx} y1={y(c.high)} y2={y(c.low)} stroke={col} strokeWidth="1.5" />
                <rect x={cx - bw / 2} y={top} width={bw} height={h} rx="1.5" fill={up ? 'var(--chart-surface, #0d0d0f)' : col} stroke={col} strokeWidth="1.5" />
                {i % labelEvery === 0 && <text className="cf-label" x={cx} y={H - 8} textAnchor="middle">{c.label}</text>}
              </g>
            );
          })}
          <line x1={m.l} x2={W - m.r} y1={y(last.close)} y2={y(last.close)} stroke="var(--c-text-1, #fff)" strokeOpacity="0.3" strokeDasharray="3 4" aria-hidden="true" />
        </svg>
      </ChartFrame>
    </>
  );
}

export default CandlestickChart;

Quick Info

Category
Charts & Stats
Filename
CandlestickChart.tsx
Dependencies
None — pure React + CSS
Tags
chartsvgzero-dependencycandlestickohlcfinancetrading