βοΈ 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-appInstall GoCharting SDK:
npm install @gocharting/chart-sdkInstall 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
π Related Documentation
- API Types Reference - Complete type definitions
- Vanilla JavaScript Example - Basic integration without React
- Time Marks Example - Adding custom time marks
- CodePen Examples - Live interactive demos
π‘ Next Steps
- Explore the Demo Repository: Clone and run the demo repositoryΒ locally
- Review Type Definitions: Check the API Types for all available interfaces
- Implement Custom Features: Build on these examples to create your own trading interface
- 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-sdkMissing 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
}, []);