TradingView Lightweight Charts Integration in dApp
TradingView Lightweight Charts is an open-source library (~45 KB gzipped) used by most DEXes for price chart display. Dydx, GMX, Uniswap Info—they either use it directly or fork it. Integration into a dApp is straightforward, but there are several places where performance breaks when working with on-chain data.
Basic Initialization
import { createChart, IChartApi, CandlestickData } from 'lightweight-charts';
const chartContainer = useRef<HTMLDivElement>(null);
const chartRef = useRef<IChartApi>();
useEffect(() => {
if (!chartContainer.current) return;
const chart = createChart(chartContainer.current, {
width: chartContainer.current.clientWidth,
height: 400,
layout: {
background: { color: '#0d0d0d' },
textColor: '#9ca3af',
},
grid: {
vertLines: { color: '#1f2937' },
horzLines: { color: '#1f2937' },
},
timeScale: {
timeVisible: true,
secondsVisible: false,
},
});
chartRef.current = chart;
return () => chart.remove();
}, []);
Always call chart.remove() in useEffect cleanup, otherwise hot reload or component unmount accumulates memory leaks.
Feeding Data from On-Chain Sources
This is the main problem when integrating with dApp. On-chain OHLCV candle data comes from several sources:
Subgraph (The Graph)—the most common option for DEX. Uniswap v3 subgraph provides poolHourDatas and poolDayDatas with OHLC for each pool. Query:
query GetCandles($pool: String!, $startTime: Int!) {
poolHourDatas(
where: { pool: $pool, periodStartUnix_gte: $startTime }
orderBy: periodStartUnix
first: 1000
) {
periodStartUnix
open
high
low
close
volumeUSD
}
}
Converting to LWC format:
const candles: CandlestickData[] = data.poolHourDatas.map((d) => ({
time: d.periodStartUnix as UTCTimestamp,
open: parseFloat(d.open),
high: parseFloat(d.high),
low: parseFloat(d.low),
close: parseFloat(d.close),
}));
candleSeries.setData(candles);
Real-time updates—poll subgraph every 30–60 seconds or WebSocket subscription to Swap events via RPC. On new event, recalculate current unclosed candle and update via candleSeries.update(newCandle) instead of setData (full data reset on every tick kills performance).
Synchronizing Multiple Charts
If you need two synchronized charts (price + volume), use chart.timeScale().subscribeVisibleTimeRangeChange() to sync viewport between instances. Standard practice for trading interfaces:
chart1.timeScale().subscribeVisibleTimeRangeChange((range) => {
if (range) chart2.timeScale().setVisibleRange(range);
});
Responsive Resizing
LWC doesn't adapt automatically on container resize. Use ResizeObserver:
const resizeObserver = new ResizeObserver(entries => {
const { width, height } = entries[0].contentRect;
chart.applyOptions({ width, height });
});
resizeObserver.observe(chartContainer.current);
Custom Markers and Overlay
To display on-chain events on chart (liquidations, large trades), use series.setMarkers():
series.setMarkers([
{
time: timestamp as UTCTimestamp,
position: 'belowBar',
color: '#ef4444',
shape: 'arrowUp',
text: 'Liquidation $2.4M',
},
]);
Markers display directly on candles and don't require custom rendering—much simpler than implementing overlay through canvas directly.







