Skip to Content
ExamplesReact + TypeScript

βš›οΈ React + TypeScript Integration

Complete guide to integrating GoCharting SDK with React and TypeScript for type-safe, production-ready chart implementations.

🌐 Live Demo & Full Implementation

GitHub Repository: gocharting-sdk-demo-craΒ  - Complete React + TypeScript examples with:

  • Basic charting components
  • Advanced trading with order management
  • Custom datafeed implementation
  • Real-time price updates
  • Position and order tracking

Reference Implementation: See ChartSDKAdvanced2.tsx in the demo repository for a complete production example.

πŸ“‹ Prerequisites

  • React 18+
  • TypeScript 5.0+
  • Node.js 16+
  • Basic knowledge of React hooks and TypeScript

πŸš€ Installation

Create new React + TypeScript app:

npx create-react-app my-trading-app --template typescript cd my-trading-app

Install GoCharting SDK:

npm install @gocharting/chart-sdk

Install additional dependencies:

npm install axios luxon

πŸ“¦ Type Imports

Import types from the SDK for full type safety:

import * as GoChartingSDK from "@gocharting/chart-sdk"; import type { ChartInstance, ChartConfig, ChartWrapper, Datafeed, SymbolInfo, Order, Trade, Position, OrderSide, BrokerAccountData, } from "@gocharting/chart-sdk";

For complete type definitions, see the API Types Reference.

🎯 Basic Chart Component

Create a simple chart component with TypeScript:

// components/BasicChart.tsx import { useEffect, useRef, useState } from "react"; import * as GoChartingSDK from "@gocharting/chart-sdk"; import type { ChartInstance, ChartConfig, ChartWrapper } from "@gocharting/chart-sdk"; export const BasicChart = () => { const chartContainerRef = useRef<HTMLDivElement>(null); const chartInstanceRef = useRef<ChartInstance | null>(null); const chartWrapperRef = useRef<ChartWrapper | null>(null); const [status, setStatus] = useState<string>("Initializing..."); useEffect(() => { const initChart = async () => { try { const chartConfig: ChartConfig = { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: createDatafeed(), // See datafeed section below licenseKey: "your-license-key", theme: "dark", onReady: (chartInstance: ChartInstance) => { chartInstanceRef.current = chartInstance; setStatus("Chart loaded successfully!"); }, onError: (error: Error) => { console.error("Chart error:", error); setStatus(`Error: ${error.message}`); }, }; chartWrapperRef.current = GoChartingSDK.createChart( "#chart-container", chartConfig ); } catch (error) { console.error("Failed to initialize chart:", error); setStatus("Failed to initialize chart"); } }; initChart(); // Cleanup on unmount return () => { if (chartWrapperRef.current && !chartWrapperRef.current.isDestroyed()) { chartWrapperRef.current.destroy(); } }; }, []); return ( <div> <div ref={chartContainerRef} id="chart-container" style={{ height: "600px", width: "100%" }} > <div>Loading chart...</div> </div> <div className="status">{status}</div> </div> ); };

πŸ“Š Custom Datafeed Implementation

Create a type-safe datafeed for your data source:

// utils/chart-datafeed.ts import type { Datafeed, SymbolInfo, Bar, ResolutionString } from "@gocharting/chart-sdk"; export const createChartDatafeed = (): Datafeed => { return { onReady: (callback) => { setTimeout(() => { callback({ supported_resolutions: ["1", "5", "15", "30", "60", "240", "1D", "1W"], supports_marks: true, supports_timescale_marks: true, supports_time: true, }); }, 0); }, searchSymbols: async ( userInput: string, exchange: string, symbolType: string, onResult: (symbols: SymbolInfo[]) => void ) => { // Implement symbol search const symbols: SymbolInfo[] = [ { symbol: "BTCUSDT", full_name: "BYBIT:FUTURE:BTCUSDT", description: "Bitcoin / USDT", exchange: "BYBIT", type: "crypto", session: "24x7", timezone: "Etc/UTC", ticker: "BTCUSDT", has_intraday: true, supported_resolutions: ["1", "5", "15", "30", "60", "240", "1D", "1W"], }, // Add more symbols ]; onResult(symbols.filter(s => s.symbol.includes(userInput.toUpperCase()))); }, resolveSymbol: async ( symbolName: string, onResolve: (symbolInfo: SymbolInfo) => void, onError: (error: string) => void ) => { try { const symbolInfo: SymbolInfo = { symbol: "BTCUSDT", full_name: "BYBIT:FUTURE:BTCUSDT", description: "Bitcoin / USDT Perpetual", exchange: "BYBIT", type: "crypto", session: "24x7", timezone: "Etc/UTC", ticker: "BTCUSDT", has_intraday: true, supported_resolutions: ["1", "5", "15", "30", "60", "240", "1D", "1W"], pricescale: 100, minmov: 1, }; onResolve(symbolInfo); } catch (error) { onError("Symbol not found"); } }, getBars: async ( symbolInfo: SymbolInfo, resolution: ResolutionString, periodParams: { from: number; to: number; firstDataRequest: boolean }, onResult: (bars: Bar[], meta: { noData: boolean }) => void, onError: (error: string) => void ) => { try { // Fetch bars from your API const response = await fetch(`https://your-api.com/bars`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ symbol: symbolInfo.ticker, resolution, from: periodParams.from, to: periodParams.to, }), }); const data = await response.json(); const bars: Bar[] = data.bars.map((bar: any) => ({ time: bar.timestamp * 1000, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })); onResult(bars, { noData: bars.length === 0 }); } catch (error) { onError("Failed to fetch bars"); } }, subscribeBars: ( symbolInfo: SymbolInfo, resolution: ResolutionString, onTick: (bar: Bar) => void, listenerGuid: string, onResetCacheNeededCallback: () => void ) => { // Implement real-time updates console.log("Subscribe to bars:", symbolInfo.symbol); }, unsubscribeBars: (listenerGuid: string) => { console.log("Unsubscribe from bars:", listenerGuid); }, }; };

πŸ“ˆ Advanced Trading Integration

Implement trading functionality with proper TypeScript types:

// components/TradingChart.tsx import { useEffect, useRef, useState, useCallback } from "react"; import * as GoChartingSDK from "@gocharting/chart-sdk"; import type { ChartInstance, ChartConfig, Order, Position, OrderSide, BrokerAccountData, } from "@gocharting/chart-sdk"; // Extract the appCallback type from ChartConfig type AppCallback = NonNullable<ChartConfig["appCallback"]>; export const TradingChart = () => { const chartInstanceRef = useRef<ChartInstance | null>(null); const chartWrapperRef = useRef<GoChartingSDK.ChartWrapper | null>(null); // Trading data refs with proper typing const currentOrderBook = useRef<Order[]>([]); const currentPositions = useRef<Position[]>([]); // App callback handler with proper typing const handleAppCallback: AppCallback = useCallback( ({ eventType, message, onClose }) => { console.log("App Callback:", eventType, message); switch (eventType) { case "PLACE_ORDER": { const orderData = message as any; // Add order to order book const newOrder: Order = { orderId: `ORDER_${Date.now()}`, datetime: new Date(), timeStamp: new Date().getTime(), status: "open", price: orderData.order?.price || 0, size: orderData.order?.size || 0, productId: orderData.order?.productId || "", remainingSize: orderData.order?.size || 0, orderType: orderData.order?.orderType || "limit", side: orderData.order?.side as OrderSide, symbol: orderData.security?.symbol || "", exchange: orderData.security?.exchange || "", // ... other required fields } as Order; currentOrderBook.current.push(newOrder); updateChartBrokerData(); break; } case "CANCEL_ORDER": { const orderData = message as any; const orderId = orderData.orderId || orderData.order?.orderId; const index = currentOrderBook.current.findIndex(o => o.orderId === orderId); if (index >= 0) { currentOrderBook.current.splice(index, 1); updateChartBrokerData(); } break; } case "MODIFY_ORDER": { const orderData = message as any; const order = orderData.order || orderData; const orderId = order.orderId; const orderIndex = currentOrderBook.current.findIndex(o => o.orderId === orderId); if (orderIndex !== -1) { const existingOrder = currentOrderBook.current[orderIndex]; if (order.price !== undefined) existingOrder.price = order.price; if (order.size !== undefined) { existingOrder.size = order.size; existingOrder.remainingSize = order.size; } updateChartBrokerData(); } break; } case "CLOSE_POSITION": { const positionData = message as any; const positionId = positionData.position?.id || positionData.id; const index = currentPositions.current.findIndex(p => p.id === positionId); if (index >= 0) { currentPositions.current.splice(index, 1); updateChartBrokerData(); } break; } default: console.log(`Chart event: ${eventType}`, message); } }, [] ); const updateChartBrokerData = useCallback(() => { if (!chartInstanceRef.current) return; const brokerData: BrokerAccountData = { accountList: [{ id: "demo-account", name: "Demo Account", balance: 100000, currency: "USDT", }], orderBook: currentOrderBook.current, tradeBook: [], positions: currentPositions.current, }; chartInstanceRef.current.setBrokerAccounts(brokerData); }, []); useEffect(() => { const initChart = async () => { const chartConfig: ChartConfig = { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: createChartDatafeed(), licenseKey: "your-license-key", theme: "dark", trading: { enableTrading: true, showReverseButton: false, }, appCallback: handleAppCallback, onReady: (chartInstance: ChartInstance) => { chartInstanceRef.current = chartInstance; updateChartBrokerData(); }, }; chartWrapperRef.current = GoChartingSDK.createChart( "#trading-chart-container", chartConfig ); }; initChart(); return () => { if (chartWrapperRef.current && !chartWrapperRef.current.isDestroyed()) { chartWrapperRef.current.destroy(); } }; }, [handleAppCallback, updateChartBrokerData]); return ( <div id="trading-chart-container" style={{ height: "600px", width: "100%" }}> <div>Loading trading chart...</div> </div> ); };

🎨 TypeScript Best Practices

Use Proper Type Imports

Always import types using the type keyword for better tree-shaking:

import type { ChartInstance, ChartConfig } from "@gocharting/chart-sdk";

Extract Complex Types

For complex callback types, extract them for reusability:

type AppCallback = NonNullable<ChartConfig["appCallback"]>; type SymbolChangeCallback = NonNullable<ChartConfig["onSymbolChange"]>;

Type Your Refs Properly

Use proper generic types for refs:

const chartInstanceRef = useRef<ChartInstance | null>(null); const chartWrapperRef = useRef<GoChartingSDK.ChartWrapper | null>(null);

Handle Async Operations

Use proper error handling with TypeScript:

try { const response = await fetch(url); const data: YourDataType = await response.json(); } catch (error) { if (error instanceof Error) { console.error("Error:", error.message); } }

Extend SDK Types When Needed

Create extended interfaces for custom properties:

interface ExtendedOrder extends Order { hidden?: boolean; customField?: string; }

πŸ”§ Common Patterns

Symbol Helper Function

Create type-safe helper functions:

const createDemoSymbolInfo = ( symbol: string, exchange: string = "BYBIT", segment: string = "FUTURE" ): SymbolInfo => { const cleanSymbol = symbol.replace(/^.*:/, ""); const fullName = `${exchange}:${segment}:${cleanSymbol}`; return { symbol: cleanSymbol, full_name: fullName, description: `${cleanSymbol} Perpetual Futures`, exchange: exchange, type: "crypto", session: "24x7", timezone: "Etc/UTC", ticker: cleanSymbol, has_intraday: true, supported_resolutions: ["1", "5", "15", "30", "60", "240", "1D", "1W"], }; };

Real-time Price Updates

Implement type-safe price updates:

const [symbolPrices, setSymbolPrices] = useState<Record<string, number>>({}); useEffect(() => { const fetchPrices = async () => { const symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT"]; for (const symbol of symbols) { try { const response = await fetch( `https://api.bybit.com/v5/market/tickers?category=linear&symbol=${symbol}` ); const data = await response.json(); if (data.result?.list?.[0]) { const price = parseFloat(data.result.list[0].lastPrice); setSymbolPrices(prev => ({ ...prev, [symbol]: price })); } } catch (error) { console.warn(`Failed to fetch price for ${symbol}:`, error); } } }; fetchPrices(); const interval = setInterval(fetchPrices, 5000); return () => clearInterval(interval); }, []);

πŸ“š Complete Example

For a complete, production-ready example with all features integrated, see:

ChartSDKAdvanced2.tsxΒ  in the demo repository.

This example includes:

  • βœ… Full TypeScript type safety
  • βœ… Trading order management
  • βœ… Position tracking with P&L calculation
  • βœ… Real-time price updates
  • βœ… Custom datafeed implementation
  • βœ… Error handling and edge cases
  • βœ… Proper cleanup on unmount

πŸ’‘ Next Steps

  1. Explore the Demo Repository: Clone and run the demo repositoryΒ  locally
  2. Review Type Definitions: Check the API Types for all available interfaces
  3. Implement Custom Features: Build on these examples to create your own trading interface
  4. Join the Community: Get help and share your implementations

πŸ› Troubleshooting

Type Errors

If you encounter type errors, ensure you’re using the latest version of the SDK:

npm update @gocharting/chart-sdk

Missing Types

If types are not being recognized, check your tsconfig.json:

{ "compilerOptions": { "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true } }

Chart Not Rendering

Ensure the container element exists before initializing:

useEffect(() => { if (!chartContainerRef.current) { console.error("Chart container not found"); return; } // Initialize chart }, []);
Last updated on