SolanaUISolanaUI

Trade Chart

A theme-aware candlestick chart built on TradingView Lightweight Charts

import { TradeChart } from "@/components/sol/trade-chart";

export function TradeChartDemo() {
  return (
    <TradeChart
      data={[
        { time: "2026-01-01", open: 148.32, high: 150.10, low: 145.60, close: 145.81 },
        { time: "2026-01-02", open: 145.81, high: 148.50, low: 144.90, close: 147.22 },
        { time: "2026-01-03", open: 147.22, high: 152.00, low: 146.80, close: 151.45 },
      ]}
      visibleBars={30}
    />
  );
}

Installation

pnpm dlx shadcn@latest add @solanaui/trade-chart
npx shadcn@latest add @solanaui/trade-chart
yarn dlx shadcn@latest add @solanaui/trade-chart

Usage

<TradeChart
  data={[
    { time: "2026-01-01", open: 148.32, high: 150.10, low: 145.60, close: 145.81 },
    { time: "2026-01-02", open: 145.81, high: 148.50, low: 144.90, close: 147.22 },
    { time: "2026-01-03", open: 147.22, high: 152.00, low: 146.80, close: 151.45 },
  ]}
  visibleBars={30}
/>

Source Code

"use client";import type {  CandlestickData,  CandlestickSeriesPartialOptions,  DeepPartial,  TimeChartOptions,} from "lightweight-charts";import { CandlestickSeries, ColorType, createChart } from "lightweight-charts";import React from "react";interface TradeChartProps {  data: CandlestickData[];  visibleBars?: number;  className?: string;  chartOptions?: DeepPartial<TimeChartOptions>;  seriesOptions?: DeepPartial<CandlestickSeriesPartialOptions>;}const LIGHT_COLORS = {  background: "rgb(255, 255, 255)",  foreground: "rgb(37, 37, 37)",  border: "rgb(235, 235, 235)",  mutedForeground: "rgb(142, 142, 142)",};const DARK_COLORS = {  background: "rgb(37, 37, 37)",  foreground: "rgb(251, 251, 251)",  border: "rgba(255, 255, 255, 0.1)",  mutedForeground: "rgb(180, 180, 180)",};const TradeChart = ({  data,  visibleBars,  className = "h-[320px] w-full",  chartOptions,  seriesOptions,}: TradeChartProps) => {  const containerRef = React.useRef<HTMLDivElement>(null);  const chartRef = React.useRef<ReturnType<typeof createChart> | null>(null);  const [isDark, setIsDark] = React.useState(() => {    if (typeof window === "undefined") return false;    return document.documentElement.classList.contains("dark");  });  // Watch for theme changes on <html> class  React.useEffect(() => {    const observer = new MutationObserver(() => {      setIsDark(document.documentElement.classList.contains("dark"));    });    observer.observe(document.documentElement, {      attributes: true,      attributeFilter: ["class"],    });    return () => observer.disconnect();  }, []);  // Stable serialized keys so inline object literals don't cause re-renders  const chartOptionsKey = JSON.stringify(chartOptions);  const seriesOptionsKey = JSON.stringify(seriesOptions);  React.useEffect(() => {    if (!containerRef.current) return;    const parsedChartOptions = chartOptionsKey      ? (JSON.parse(chartOptionsKey) as DeepPartial<TimeChartOptions>)      : undefined;    const parsedSeriesOptions = seriesOptionsKey      ? (JSON.parse(          seriesOptionsKey,        ) as DeepPartial<CandlestickSeriesPartialOptions>)      : undefined;    const colors = isDark ? DARK_COLORS : LIGHT_COLORS;    const defaultChartOptions: DeepPartial<TimeChartOptions> = {      layout: {        textColor: colors.foreground,        background: {          type: ColorType.Solid as const,          color: colors.background,        },        fontFamily: "var(--font-geist-sans)",      },      grid: {        vertLines: { color: colors.border, style: 1 },        horzLines: { color: colors.border, style: 1 },      },      crosshair: {        vertLine: {          color: colors.mutedForeground,          width: 1,          style: 3,        },        horzLine: {          color: colors.mutedForeground,          width: 1,          style: 3,        },      },      rightPriceScale: { borderColor: colors.border },      timeScale: { borderColor: colors.border, timeVisible: true },      autoSize: true,      ...parsedChartOptions,    };    chartRef.current = createChart(containerRef.current, defaultChartOptions);    const candlestick = chartRef.current.addSeries(CandlestickSeries, {      borderVisible: false,      ...parsedSeriesOptions,    });    candlestick.setData(data);    if (visibleBars && data.length > visibleBars) {      chartRef.current.timeScale().setVisibleLogicalRange({        from: data.length - visibleBars - 0.5,        to: data.length - 0.5,      });    } else {      chartRef.current.timeScale().fitContent();    }    return () => {      chartRef.current?.remove();    };  }, [data, visibleBars, isDark, chartOptionsKey, seriesOptionsKey]);  return <div className={className} ref={containerRef} />;};export type { TradeChartProps };export { TradeChart };