name: chart-setup description: Wire TradingView lightweight-charts into a React app — candlestick + volume series, dark theme, dynamic resize. Use when the user wants to add a price chart, swap a Recharts/Chart.js chart for lightweight-charts, or build a custom indicator on top of an existing chart.
chart-setup
Add a production-quality price chart to a React app using TradingView's
lightweight-charts library.
This is what powers the main ChartCanvas in command-dash.
When to use
Trigger this skill whenever the user wants to:
- Show an OHLC / candlestick chart for a stock or crypto symbol
- Add volume bars under a price chart
- Theme an existing lightweight-charts instance to a dark dashboard palette
- Wire a chart to a
fetch('/api/...')source that returns{ time, open, high, low, close, volume }[]
Install
npm install lightweight-charts
lightweight-charts v5+ uses the modular series API (addSeries(CandlestickSeries, ...)) — older v4 code that calls addCandlestickSeries(...) will not work. If the project is on v4, either upgrade to v5 or use the v4 API throughout.
Drop-in component
Create src/components/ChartPanel.tsx:
import { useEffect, useRef, useState } from 'react'
import {
createChart,
CandlestickSeries,
HistogramSeries,
type IChartApi,
type ISeriesApi
} from 'lightweight-charts'
interface Bar {
time: number // unix seconds (UTCTimestamp)
open: number
high: number
low: number
close: number
volume: number
}
interface Props {
symbol: string
fetchBars: (symbol: string) => Promise<Bar[]>
}
export function ChartPanel({ symbol, fetchBars }: Props) {
const containerRef = useRef<HTMLDivElement | null>(null)
const chartRef = useRef<IChartApi | null>(null)
const candleRef = useRef<ISeriesApi<'Candlestick'> | null>(null)
const volRef = useRef<ISeriesApi<'Histogram'> | null>(null)
useEffect(() => {
if (!containerRef.current) return
const chart = createChart(containerRef.current, {
layout: { background: { color: '#1d2939' }, textColor: '#98a2b3' },
grid: { vertLines: { color: '#293548' }, horzLines: { color: '#293548' } },
rightPriceScale: { borderColor: '#344054' },
timeScale: { borderColor: '#344054', timeVisible: true, secondsVisible: false },
crosshair: { mode: 1 },
autoSize: true
})
candleRef.current = chart.addSeries(CandlestickSeries, {
upColor: '#12b76a', downColor: '#f04438',
wickUpColor: '#12b76a', wickDownColor: '#f04438',
borderVisible: false
})
volRef.current = chart.addSeries(HistogramSeries, {
priceFormat: { type: 'volume' },
priceScaleId: 'volume_scale',
color: '#344054'
})
chart.priceScale('volume_scale').applyOptions({ scaleMargins: { top: 0.82, bottom: 0 } })
chartRef.current = chart
return () => chart.remove()
}, [])
useEffect(() => {
let cancelled = false
fetchBars(symbol).then((bars) => {
if (cancelled || !candleRef.current || !volRef.current) return
candleRef.current.setData(bars.map((b) => ({
time: b.time as never, open: b.open, high: b.high, low: b.low, close: b.close
})))
volRef.current.setData(bars.map((b) => ({
time: b.time as never,
value: b.volume,
color: b.close >= b.open ? 'rgba(18, 183, 106, 0.35)' : 'rgba(240, 68, 56, 0.35)'
})))
chartRef.current?.timeScale().fitContent()
})
return () => { cancelled = true }
}, [symbol, fetchBars])
return <div ref={containerRef} className="w-full h-full" />
}
Key requirements
autoSize: trueincreateChartoptions — fills its container and re-fits on resize. No manualresizeObserverplumbing required.- Container must have an explicit height —
h-fullonly works if every ancestor is also height-constrained (flexbox or grid). timeis unix seconds (UTCTimestamp) — not milliseconds, not ISO strings. Divide JSDate.now()by 1000.- Sort bars ascending by
timebefore callingsetData. Polygon and most providers return ascending if you passsort=asc. - Cast
timewithas neverin TS to avoid type friction with lightweight-charts' brandedTimetype.
Color palette (TradersPost dark navy)
| Token | Value | Use |
|---|---|---|
bg-card |
#1d2939 |
chart background |
border |
#344054 |
axis + scale borders |
text-secondary |
#98a2b3 |
axis text |
| grid lines | #293548 |
both vert + horz |
| up candle / bull | #12b76a |
wickUp, body up |
| down candle / bear | #f04438 |
wickDown, body down |
| accent / line | #2e90fa |
overlay lines, equity curves |
Reference
The canonical implementation in command-dash is apps/web/src/components/dashboard/ChartCanvas.tsx. It includes:
- Controlled props for symbol, timeframe, chart type (Candles / Bars / Line / Area / Heikin Ashi), and indicator overlays (SMA / EMA)
- Vite proxy to a FastAPI backend that returns Polygon OHLC
- A
requestAnimationFramesize-wait pattern so charts created before the grid layout finalizes still render correctly
For equity-curve charts (single line series, no volume), see the OverviewTab inside apps/web/src/components/dashboard/StrategyReport.tsx.