rn-tradingview
v2.3.0
Published
Production-grade multi-exchange candlestick chart for React Native and React (web). Supports Binance, OKX, and Bybit. GPU-accelerated on native (Skia), lightweight-charts on web. Reanimated v3 gestures, technical indicators (MACD, RSI, Bollinger Bands), d
Maintainers
Keywords
Readme
rn-tradingview
Production-grade multi-exchange trading chart for React Native.
rn-tradingview is a free, open-source React Native trading chart engine that connects to Binance, OKX, Bybit, or any custom exchange out of the box. GPU-accelerated native rendering, Reanimated v3 gestures, real-time WebSocket candles + order book, full-featured technical indicators, drawing tools, and depth charts — all in a single library.
Built for crypto trading apps, DeFi dashboards, portfolio trackers, and any React Native app that needs a professional financial chart.
Why rn-tradingview?
| | rn-tradingview | WebView-based libs | |---|---|---| | Rendering | GPU-accelerated native | Web / WebView | | Performance | 60 FPS on 10 000+ candles | Laggy on large datasets | | Gestures | Native Reanimated v3 | JS-bridge events | | Exchanges | Binance + OKX + Bybit + Custom | Manual wiring | | Offline | Yes (bring your own data) | Needs web bundle | | TypeScript | Full coverage | Partial |
Features
- Multi-exchange, zero config — plug in
exchange="binance","okx", or"bybit"and get live candles + order book automatically - Custom exchange adapters — wire any REST + WebSocket feed in ~20 lines with
createExchangeAdapter - GPU-accelerated rendering — native chart renderer with Skia canvas fallback, never blocks the JS thread
- Reanimated v3 gestures — inertial pan, pinch-zoom around focal point, long-press crosshair that snaps to nearest candle
- Technical indicators
- Main / overlay:
MA,EMA,BOLL(Bollinger Bands),SAR(Parabolic SAR),AVL(volume MA),SUPER(Supertrend) - Sub-pane:
MACD,RSI,KDJ,OBV,WR(Williams %R),StochRSI
- Main / overlay:
- Drawing tools — Trend line, Ray, Horizontal line, Fibonacci retracement, Channel, Measure; sidebar modeled after Binance's drawing toolbar
- Depth / order book chart — cumulative bid & ask visualization with live WebSocket updates, switchable between exchanges
- Infinite scroll history — scroll left to auto-paginate older candles
- Fullscreen & orientation — portrait ↔ landscape with optional
react-native-orientation-locker - Bring-your-own data — use any REST API or WebSocket feed with
CandlestickChartorBinanceChart - Full TypeScript — every prop, candle shape, indicator config, and theme token is typed
Installation
npm install rn-tradingview
# or
yarn add rn-tradingviewPeer dependencies
npm install \
react-native-gesture-handler \
react-native-reanimated \
react-native-safe-area-context \
react-native-svg| Package | Used for |
|---|---|
| react-native-gesture-handler | GestureHandlerRootView wrapper + Gesture / GestureDetector for pan, pinch-zoom, crosshair, and drawing gestures |
| react-native-reanimated | Viewport shared values, inertial pan (withDecay), animated overlay positions — all run on the UI thread |
| react-native-safe-area-context | useSafeAreaInsets() for iPhone notch / Dynamic Island padding in fullscreen and header |
| react-native-svg | SVG icons in the drawing toolbar and sidebar; depth chart bid/ask curve paths |
Optional:
npm install react-native-orientation-lockerOnly needed if you want the chart to lock to landscape when entering fullscreen. The chart works without it — fullscreen opens but won't lock orientation.
cd ios && pod installAdd the Reanimated Babel plugin:
// babel.config.js
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};Quick Start
TradingChart — live chart, one line per exchange
TradingChart manages WebSocket subscriptions, candle state, ticker, order book, and drawing persistence internally.
Binance
import { TradingChart } from 'rn-tradingview';
<TradingChart exchange="binance" pair="BTCUSDT" />OKX
import { TradingChart } from 'rn-tradingview';
<TradingChart exchange="okx" pair="BTC-USDT" />Bybit
import { TradingChart } from 'rn-tradingview';
<TradingChart exchange="bybit" pair="BTCUSDT" />Full example:
import React from 'react';
import { SafeAreaView } from 'react-native';
import { TradingChart } from 'rn-tradingview';
export default function App() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#0B0F14' }}>
<TradingChart
exchange="binance" // 'binance' | 'okx' | 'bybit'
pair="BTCUSDT"
defaultTimeframe="1h" // initial timeframe — user can change in the UI
height={500} // omit to fill the parent via flex:1
theme="dark" // 'dark' | 'light'
onTickerChange={(data) => console.log('price:', data.lastPrice)}
onReady={() => console.log('first batch rendered')}
/>
</SafeAreaView>
);
}CandlestickChart — bring your own data
Use this when you already have candle data from your own API or WebSocket.
import React from 'react';
import { SafeAreaView } from 'react-native';
import { CandlestickChart } from 'rn-tradingview';
import type { Candle } from 'rn-tradingview';
// Candle = { t: ms timestamp, o, h, l, c, v } — oldest → newest
const candles: Candle[] = [
{ t: 1700000000000, o: 36000, h: 36500, l: 35800, c: 36200, v: 120.5 },
{ t: 1700003600000, o: 36200, h: 37000, l: 36100, c: 36800, v: 98.3 },
];
export default function App() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#0B0F14' }}>
<CandlestickChart
candles={candles}
symbol="BTCUSDT"
interval="1h"
height={450}
themeMode="dark"
onTimeframeChange={(tf) => console.log('timeframe:', tf)}
onLoadMore={(oldestTimestamp) => { /* fetch older pages */ }}
/>
</SafeAreaView>
);
}BinanceChart — stateless low-level chart
BinanceChart is the lowest-level stateless chart component. Pass raw RawCandle[] or Candle[] data directly with no exchange wiring.
import { BinanceChart } from 'rn-tradingview';
<BinanceChart
data={candles} // RawCandle[] | Candle[], oldest → newest
symbol="BTCUSDT"
interval="1h"
height={400}
theme="dark" // 'dark' | 'light' | ChartThemeTokens
indicators={['MA', 'RSI']} // indicator IDs to enable
onTimeframeChange={(tf) => console.log(tf)}
onCrosshairMove={(candle) => console.log(candle)}
/>Exchange Adapters
rn-tradingview ships purpose-built adapters for Binance, OKX, and Bybit. Each adapter exposes fetchCandles (REST) and subscribeCandles / subscribeTicker (WebSocket) with a normalized candle shape.
Binance adapter
import { binanceAdapter } from 'rn-tradingview';
// REST — fetch historical candles
const candles = await binanceAdapter.fetchCandles('BTCUSDT', '1h', 500);
// WebSocket — live candle stream
const unsub = binanceAdapter.subscribeCandles('BTCUSDT', '1m', (candle) => {
console.log('Binance candle:', candle);
});
// Live ticker (price updates)
const unsubTicker = binanceAdapter.subscribeTicker('BTCUSDT', (ticker) => {
console.log('Binance price:', ticker.price);
});
unsub();
unsubTicker();Symbol format:
BTCUSDT(no separator, uppercase) REST:https://api.binance.com/api/v3/klinesWS:wss://stream.binance.com:9443/ws/<symbol>@kline_<interval>
OKX adapter
import { okxAdapter } from 'rn-tradingview';
const candles = await okxAdapter.fetchCandles('BTC-USDT', '1H', 300);
const unsub = okxAdapter.subscribeCandles('BTC-USDT', '1m', (candle) => {
console.log('OKX candle:', candle);
});
const unsubTicker = okxAdapter.subscribeTicker('BTC-USDT', (ticker) => {
console.log('OKX price:', ticker.price, '24h vol:', ticker.volume24h);
});
unsub();
unsubTicker();Symbol format:
BTC-USDT(hyphen-separated, uppercase) REST:https://www.okx.com/api/v5/market/candlesWS:wss://wspri.okx.com:8443/ws/v5/ipublic
Bybit adapter
import { bybitAdapter } from 'rn-tradingview';
const candles = await bybitAdapter.fetchCandles('BTCUSDT', '1h', 200);
const unsub = bybitAdapter.subscribeCandles('BTCUSDT', '1m', (candle) => {
console.log('Bybit candle:', candle);
});
const unsubTicker = bybitAdapter.subscribeTicker('BTCUSDT', (ticker) => {
console.log('Bybit price:', ticker.price);
});
unsub();
unsubTicker();Symbol format:
BTCUSDT(no separator, uppercase) REST:https://api.bybit.com/v5/market/klineWS:wss://stream.bybit.com/v5/public/spot
Custom exchange adapter
Wire any exchange — or your own proprietary data feed — with createExchangeAdapter:
import { createExchangeAdapter, CandlestickChart, mergeLiveCandle } from 'rn-tradingview';
import type { Candle } from 'rn-tradingview';
const myAdapter = createExchangeAdapter({
name: 'MyExchange',
async fetchCandles(symbol, interval, limit = 200, endTime) {
const res = await fetch(
`https://api.myexchange.com/candles?symbol=${symbol}&tf=${interval}&limit=${limit}`
);
const rows = await res.json();
// Return oldest → newest, timestamps in ms
return rows.map((r: any): Candle => ({
t: r.timestamp, o: r.open, h: r.high, l: r.low, c: r.close, v: r.volume,
}));
},
subscribeCandles(symbol, interval, onUpdate) {
const ws = new WebSocket('wss://ws.myexchange.com/stream');
ws.onopen = () =>
ws.send(JSON.stringify({ type: 'subscribe', channel: `candle:${symbol}:${interval}` }));
ws.onmessage = (e) => {
const d = JSON.parse(e.data);
onUpdate({ t: d.t, o: d.o, h: d.h, l: d.l, c: d.c, v: d.v });
};
return () => ws.close();
},
normalizeSymbol: (s) => s.toUpperCase(),
normalizeInterval: (i) => i,
});
// Use with CandlestickChart:
export default function MyChart() {
const [candles, setCandles] = React.useState<Candle[]>([]);
React.useEffect(() => {
myAdapter.fetchCandles('BTCUSDT', '1m').then(setCandles);
return myAdapter.subscribeCandles('BTCUSDT', '1m', (c) => {
setCandles((prev) => mergeLiveCandle(prev, c));
});
}, []);
return <CandlestickChart candles={candles} symbol="BTCUSDT" interval="1m" height={450} />;
}Candle Format
All exchange adapters and chart components share the same Candle shape:
type Candle = {
t: number; // Unix timestamp in milliseconds
o: number; // Open
h: number; // High
l: number; // Low
c: number; // Close
v: number; // Volume (base asset)
};All candle arrays are oldest → newest (ascending t).
The library also exports NormalizedCandle ({ time, open, high, low, close, volume }) for internal renderer use — you will not normally need it.
Depth / Order Book Chart
Single exchange:
import { DepthChart } from 'rn-tradingview';
<DepthChart
exchange="binance" // 'binance' | 'okx' | 'bybit'
symbol="BTCUSDT"
height={300}
/>Multi-exchange with built-in toggle (Binance / OKX / Bybit):
import { ExchangeDepthChart } from 'rn-tradingview';
<ExchangeDepthChart
defaultExchange="binance"
symbol="BTCUSDT"
symbolByExchange={{ okx: 'BTC-USDT', bybit: 'BTCUSDT', binance: 'BTCUSDT' }}
height={300}
/>ExchangeDepthChart renders a Binance / OKX / Bybit toggle bar above the chart and switches the live WebSocket automatically.
OKX — useOkxCandles hook with infinite scroll
import React, { useEffect } from 'react';
import { SafeAreaView, ActivityIndicator } from 'react-native';
import { CandlestickChart, useOkxCandles } from 'rn-tradingview';
export default function OkxScreen() {
const { candles, loading, loadingMore, fetchInitialCandles, fetchMoreCandles } =
useOkxCandles({ instId: 'BTC-USDT', bar: '1m' });
useEffect(() => { void fetchInitialCandles(); }, []);
if (loading) return <ActivityIndicator />;
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#0B0F14' }}>
<CandlestickChart
candles={candles}
symbol="BTC-USDT"
interval="1m"
height={450}
onLoadMore={(oldestTimestamp) => void fetchMoreCandles(oldestTimestamp)}
/>
</SafeAreaView>
);
}Exchange Support
| Feature | Binance | OKX | Bybit | Custom |
|---------|:-------:|:---:|:-----:|:------:|
| REST candles | ✅ | ✅ | ✅ | ✅ |
| Live candle WebSocket | ✅ | ✅ | ✅ | ✅ |
| Live ticker WebSocket | ✅ | ✅ | ✅ | optional |
| Order book WebSocket | ✅ | ✅ | ✅ | — |
| useOkxCandles hook | — | ✅ | — | — |
| Infinite scroll history | ✅ | ✅ | ✅ | — |
| Exchange adapter | binanceAdapter | okxAdapter | bybitAdapter | createExchangeAdapter |
| Symbol format | BTCUSDT | BTC-USDT | BTCUSDT | yours |
API Reference
<TradingChart>
All-in-one component: manages WebSocket connections, candle buffer, ticker, order book, and drawing persistence. Recommended for live exchange integrations.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| exchange | 'binance' \| 'okx' \| 'bybit' | 'binance' | Exchange to connect to |
| pair | string | 'BNB/USDT' | Trading pair — any form: 'BTCUSDT', 'BTC-USDT', 'BTC/USDT' |
| defaultTimeframe | string | '1m' | Initial timeframe; the user can change it via the UI |
| theme | 'dark' \| 'light' | 'dark' | Color theme |
| height | number | flex | Fixed height in px. Omit to fill the parent via flex: 1 |
| watermark | { name?: string; image?: any } | — | Watermark text and/or logo shown in the chart body |
| pairs | string[] | — | Pair list for the built-in pair switcher |
| onPairChange | (pair: string) => void | — | Called when the user picks a different pair |
| onTickerChange | (data: MarketTickerData) => void | — | Called on each live ticker update |
| onReady | () => void | — | Fires once after the first candle batch renders for the current pair/timeframe |
<CandlestickChart>
Stateless chart — you own the candle data. Accepts data from any exchange or API.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| candles | Candle[] | required | OHLCV array, oldest → newest |
| symbol | string | — | Label shown in the header |
| interval | string | — | Active timeframe label |
| height | number | — | Chart height in px |
| width | number | full | Chart width in px |
| themeMode | 'dark' \| 'light' | 'dark' | Color theme |
| loading | boolean | false | Shows loading overlay while initial data fetches |
| watermark | string | — | Watermark text shown in the chart body |
| pricePrecision | number | auto | Force displayed price decimal places |
| onTimeframeChange | (tf: string) => void | — | Called when the user picks a timeframe chip |
| onLoadMore | (oldestTs: number) => void | — | Called when user scrolls past the oldest candle |
| onInteractionStateChange | (isInteracting: boolean) => void | — | true during pan/crosshair/draw, false when idle |
| timeframes | readonly string[] | built-in | Override the timeframe chip list |
| contentInset | { top?, bottom?, left?, right? } | — | Safe-area padding override |
| showHeader | boolean | true | Show expanded OHLC header |
| showSidebar | boolean | true | Show the left drawing sidebar |
| showDrawingTools | boolean | true | Show drawing tool controls |
| showVolume | boolean | true | Show the volume pane |
| showIndicators | boolean | true | Show all indicator panes and legends |
| chartType | 'original' \| 'depth' | 'original' | Controlled chart mode |
| onChartTypeChange | (type: 'original' \| 'depth') => void | — | Called when user switches chart mode |
| indicatorTrigger | number | — | Increment to programmatically open the indicator sheet |
| activeMainIndicators | string[] | — | External control of overlay indicators ('MA', 'EMA', etc.) |
| activeSubIndicators | string[] | — | External control of sub-pane indicators ('RSI', 'MACD', etc.) |
| exchange | 'binance' \| 'okx' \| 'bybit' | — | Exchange for depth mode order book |
| useNativeChart | boolean | — | Enable native chart renderer with JS fallback |
<BinanceChart>
Lowest-level stateless chart. Accepts raw RawCandle[] or Candle[] directly with no exchange wiring.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| data | RawCandle[] \| Candle[] | required | OHLCV array, oldest → newest |
| symbol | string | — | Label shown in the header |
| interval | string | — | Timeframe label |
| height | number | — | Chart height in px |
| width | number | full | Chart width in px |
| theme | 'dark' \| 'light' \| ChartThemeTokens | 'dark' | Color theme or raw token object |
| indicators | IndicatorId[] | — | Indicator IDs to enable, e.g. ['MA', 'RSI', 'MACD'] |
| indicatorParams | IndicatorConfig | — | Per-indicator period / style overrides |
| onTimeframeChange | (tf: string) => void | — | Called when user picks a timeframe |
| onCrosshairMove | (candle: Candle \| null) => void | — | Called when the crosshair moves |
<DepthChart>
Single-exchange live order book.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| exchange | 'binance' \| 'okx' \| 'bybit' | 'binance' | Exchange to connect |
| symbol | string | required | Trading pair |
| height | number | — | Chart height |
| maxLevelsPerSide | number | 100 | Max bid/ask price levels |
| throttleMs | number | — | WebSocket update throttle in ms |
<ExchangeDepthChart>
Depth chart with a built-in Binance / OKX / Bybit toggle.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| defaultExchange | 'binance' \| 'okx' \| 'bybit' | 'binance' | Initial exchange |
| symbol | string | — | Fallback symbol for all exchanges |
| symbolByExchange | { binance?, okx?, bybit? } | — | Per-exchange symbol overrides |
| height | number | — | Chart height |
Exchange adapter interface
interface ExchangeAdapter {
readonly name: string;
fetchCandles(
symbol: string,
interval: string,
limit?: number,
endTime?: number, // Unix ms — fetch candles OLDER than this for pagination
): Promise<Candle[]>;
subscribeCandles(
symbol: string,
interval: string,
onUpdate: (candle: Candle) => void,
): () => void; // returns unsubscribe
subscribeTicker?(
symbol: string,
onUpdate: (ticker: LiveTicker) => void,
): () => void;
normalizeSymbol(symbol: string): string;
normalizeInterval(interval: string): string;
}
interface LiveTicker {
price: number;
timestamp: number;
volume24h?: number;
change24h?: number;
changePct24h?: number;
}createExchangeAdapter
import { createExchangeAdapter } from 'rn-tradingview';
const adapter = createExchangeAdapter({
name: 'MyExchange',
fetchCandles: async (symbol, interval, limit, endTime) => { /* return Candle[] */ },
subscribeCandles: (symbol, interval, onUpdate) => { /* return () => ws.close() */ },
subscribeTicker: (symbol, onUpdate) => { /* optional */ return () => ws.close(); },
normalizeSymbol: (s) => s.toUpperCase(),
normalizeInterval: (i) => i,
});useOkxCandles hook
const {
candles, // Candle[]
loading, // initial fetch in progress
loadingMore, // pagination in progress
error, // Error | null
hasMore, // more history available
fetchInitialCandles, // () => Promise<void>
fetchMoreCandles, // (oldestTs?: number) => Promise<void>
applyLiveCandle, // (candle: Candle) => void
reset, // () => void
} = useOkxCandles({
instId: 'BTC-USDT', // OKX instrument ID
bar: '1m', // timeframe
initialLimit: 300, // candles to fetch on first load (max 300)
pageLimit: 100, // candles per history page
});MarketTickerData type
// Returned by TradingChart's onTickerChange
type MarketTickerData = {
lastPrice: number;
changePercent: number;
highPrice: number;
lowPrice: number;
volume: number; // base-asset volume
quoteVolume: number; // quote-asset volume
};Data utilities
import {
fetchKlines, // REST candles (Binance-backed)
subscribeKlines, // live candle WebSocket (Binance)
fetchDepth, // order book snapshot (Binance)
subscribeDepth, // live depth stream (Binance)
mergeLiveCandle, // merge a forming candle into a candle array
mergeCandlesSafely, // safe merge without duplicates
} from 'rn-tradingview';
// Fetch candles via Binance REST
const candles = await fetchKlines('BTC-USDT', '1h', 300);
// Subscribe to live candles (returns { close() })
const sub = subscribeKlines('BTC-USDT', '1m', (candle) => {
setCandles(prev => mergeLiveCandle(prev, candle));
});
sub.close(); // cleanupIndicator engine
import { computeChartIndicators } from 'rn-tradingview';
import type { IndicatorRuntimeParams, IndicatorComputeFlags } from 'rn-tradingview';
// params — per-indicator period overrides (all optional)
const params: IndicatorRuntimeParams = {
maPeriods: [7, 25, 99],
emaPeriod: 21,
rsiPeriod: 14,
macd: { fast: 12, slow: 26, signal: 9 },
bollinger: { period: 20, stdDev: 2 },
kdj: { n: 9 },
wrPeriod: 14,
stochRsi: { rsiLen: 14, stochLen: 14 },
sar: { step: 0.02, max: 0.2 },
supertrend: { period: 10, multiplier: 3 },
volumeMAPeriod: 20,
};
// flags — skip indicators you don't need (significant performance gain on large datasets)
const flags: IndicatorComputeFlags = {
ma: true, ema: true, rsi: true, macd: true,
bollinger: false, kdj: false, obv: false,
};
const result = computeChartIndicators(candles, /* active */ true, params, flags);
// Result shape — each field is (number | null)[] aligned with the candles array
// result.ma7, result.ma25, result.ma99
// result.ema21
// result.rsi14
// result.macdLine, result.macdSignal, result.macdHistogram
// result.kdjK, result.kdjD, result.kdjJ
// result.bb.upper, result.bb.middle, result.bb.lower
// result.superUp, result.superDown
// result.sar, result.obvLine, result.vwapLine, result.volMA
// result.williamsR, result.stochRsiK, result.stochRsiDTheming
import { TradingChart, CandlestickChart, BinanceChart, DarkTheme, LightTheme } from 'rn-tradingview';
import type { ChartThemeTokens } from 'rn-tradingview';
// Named themes via prop
<TradingChart exchange="binance" pair="BTCUSDT" theme="dark" />
<CandlestickChart candles={candles} themeMode="light" />
// Fully custom token object (BinanceChart only)
const myTheme: ChartThemeTokens = {
...DarkTheme,
up: '#00C087',
down: '#FF4D4D',
background: '#000000',
};
<BinanceChart data={candles} theme={myTheme} />UI Sheet components
IndicatorsSheet and ChartSettingsSheet are exported for apps that want to render the built-in indicator / settings panels in their own modal flow.
import { IndicatorsSheet, ChartSettingsSheet } from 'rn-tradingview';
<IndicatorsSheet
visible={showIndicators}
onClose={() => setShowIndicators(false)}
activeMainIndicators={activeMain}
activeSubIndicators={activeSub}
onToggle={(id) => toggleIndicator(id)}
/>React Native Compatibility
| rn-tradingview | React Native | React | |----------------|-------------|-------| | 2.x | 0.72 – 0.85 | 18–19 | | 1.x | 0.72 – 0.75 | 18 |
License
MIT © Salil Samdarshy & Shubham Narula
