Framework and Widget Starters
The Quickstart chart binds cleanly to any component lifecycle: your framework owns create and destroy in one place, and the SDK owns everything between them. The same binding carries the standalone widgets — market depth, time and sales, watchlist, order ticket, and the full trading terminal.
Complete the shared prerequisites once, then open your framework's tab. Every tab assumes them.
Shared prerequisites
- Complete Package access, install the private SDK under
the
@tradescript/proalias, and import its styles once at the application entry point:
npm install --save-exact '@tradescript/pro@npm:@tradescript/charts-pro-acme-production-a1b2c@0.1.1'
import '@tradescript/pro/style.css';
import '@tradescript/pro/tailwind.css';
-
Export a stable
MarketDataFeedasdatafeedfrom adatafeedmodule. ImplementloadBarsfirst, then add search, realtime, quotes, depth, news, or trading capabilities as your product needs them. See Datafeeds for the contract. -
Give the mount element a real height. A zero-height container renders nothing.
-
Every direct-mount starter calls
destroy()when its framework unmounts the component so chart DOM, workers, and market-data subscriptions are released together. The optional React component performs that teardown when it unmounts.
The lifecycle examples below import deploymentLease from application bootstrap. That value is the backend-cached shared lease, never the permanent API credential. createTradeScriptSdk({ lease }) verifies it before the first product surface mounts and returns the authorization-bound TradeScriptSdk; see Customer authorization.
Pick your framework
- Plain JavaScript
- React
- Vue 3
- Svelte
- Angular
- Next.js
- Nuxt 3
Use the core lifecycle directly in a browser application or any frontend framework that gives you an HTML element.
import { createTradeScriptSdk } from '@tradescript/pro/sdk/core';
import { datafeed } from './datafeed';
import '@tradescript/pro/style.css';
import '@tradescript/pro/tailwind.css';
declare const deploymentLease: string;
const sdk = await createTradeScriptSdk({ lease: deploymentLease });
const mounted = sdk.chart.mount({
mount: '#chart',
symbol: { ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' },
interval: '15m',
datafeed,
theme: 'dark',
});
const widget = await mounted.ready();
await widget.chart().dataReady();
// Call this when the page, route, or owning view is removed.
export function destroyChart() {
mounted.destroy();
}
<div id="chart" style="height: 640px"></div>
Use the first-class React widget when the host already renders React.
npm install react react-dom
import { TradeScriptWidget } from '@tradescript/pro/react';
import { createTradeScriptSdk } from '@tradescript/pro/sdk/core';
import { datafeed } from './datafeed';
import '@tradescript/pro/style.css';
import '@tradescript/pro/tailwind.css';
import '@tradescript/pro/react/style.css';
declare const deploymentLease: string;
const sdk = await createTradeScriptSdk({ lease: deploymentLease });
export function TradeScriptChart() {
return (
<div style={{ height: 640 }}>
<TradeScriptWidget
sdk={sdk}
symbol={{ ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' }}
interval="15m"
datafeed={datafeed}
theme="dark"
/>
</div>
);
}
Use the component's onMount callback when the surrounding React component
needs the mounted chart handle. See Use TradeScript with React.
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { createTradeScriptSdk, type ChartSdkMount } from '@tradescript/pro/sdk/core';
import { datafeed } from '../datafeed';
import '@tradescript/pro/style.css';
import '@tradescript/pro/tailwind.css';
const mount = ref<HTMLElement | null>(null);
let widget: ChartSdkMount | undefined;
declare const deploymentLease: string;
onMounted(async () => {
const sdk = await createTradeScriptSdk({ lease: deploymentLease });
widget = sdk.chart.mount({
mount: mount.value!,
symbol: { ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' },
interval: '15m',
datafeed,
theme: 'dark',
});
await widget.ready();
});
onBeforeUnmount(() => widget?.destroy());
</script>
<template>
<div ref="mount" class="chart" />
</template>
<style scoped>
.chart { height: 640px; }
</style>
<script lang="ts">
import { onMount } from 'svelte';
import { createTradeScriptSdk, type ChartSdkMount } from '@tradescript/pro/sdk/core';
import { deploymentLease } from './bootstrap';
import { datafeed } from './datafeed';
import '@tradescript/pro/style.css';
import '@tradescript/pro/tailwind.css';
let mount: HTMLDivElement;
let widget: ChartSdkMount | undefined;
onMount(() => {
let disposed = false;
void createTradeScriptSdk({ lease: deploymentLease }).then(async (sdk) => {
if (disposed) return;
widget = sdk.chart.mount({
mount,
symbol: { ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' },
interval: '15m',
datafeed,
theme: 'dark',
});
await widget.ready();
});
return () => {
disposed = true;
widget?.destroy();
};
});
</script>
<div class="chart" bind:this={mount}></div>
<style>
.chart { height: 640px; }
</style>
Import the SDK styles from the application-level styles.css, not an encapsulated component stylesheet:
@import '@tradescript/pro/style.css';
@import '@tradescript/pro/tailwind.css';
import {
AfterViewInit,
Component,
ElementRef,
OnDestroy,
ViewChild,
} from '@angular/core';
import { createTradeScriptSdk, type ChartSdkMount } from '@tradescript/pro/sdk/core';
import { datafeed } from './datafeed';
declare const deploymentLease: string;
@Component({
selector: 'app-tradescript-chart',
standalone: true,
template: '<div #mount class="chart"></div>',
styles: ['.chart { height: 640px; }'],
})
export class TradeScriptChartComponent implements AfterViewInit, OnDestroy {
@ViewChild('mount', { static: true }) mount!: ElementRef<HTMLElement>;
private widget?: ChartSdkMount;
async ngAfterViewInit() {
const sdk = await createTradeScriptSdk({ lease: deploymentLease });
this.widget = sdk.chart.mount({
mount: this.mount.nativeElement,
symbol: { ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' },
interval: '15m',
datafeed,
theme: 'dark',
});
await this.widget.ready();
}
ngOnDestroy() {
this.widget?.destroy();
}
}
For Angular SSR, keep this component on the browser path or guard its render with isPlatformBrowser; the chart needs a real DOM mount.
Import global SDK styles from the root layout:
import '@tradescript/pro/style.css';
import '@tradescript/pro/tailwind.css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return <html lang="en"><body>{children}</body></html>;
}
Mount the chart from a Client Component. The dynamic SDK import runs inside useEffect, so server rendering never tries to create a browser chart.
'use client';
import { useEffect, useRef } from 'react';
import { deploymentLease } from './bootstrap';
import { datafeed } from './datafeed';
export function TradeScriptChart() {
const mount = useRef<HTMLDivElement>(null);
useEffect(() => {
let disposed = false;
let destroy: (() => void) | undefined;
void import('@tradescript/pro/sdk/core').then(async ({ createTradeScriptSdk }) => {
const sdk = await createTradeScriptSdk({ lease: deploymentLease });
if (disposed || !mount.current) return;
const widget = sdk.chart.mount({
mount: mount.current,
symbol: { ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' },
interval: '15m',
datafeed,
theme: 'dark',
});
destroy = () => widget.destroy();
await widget.ready();
});
return () => {
disposed = true;
destroy?.();
};
}, []);
return <div ref={mount} style={{ height: 640 }} />;
}
Do not disable SSR for the entire page. Keep only the chart component client owned so the rest of the route can retain normal server rendering.
Register the global SDK styles once:
export default defineNuxtConfig({
css: [
'@tradescript/pro/style.css',
'@tradescript/pro/tailwind.css',
],
});
The .client.vue suffix makes the chart component browser-only.
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import type { ChartSdkMount } from '@tradescript/pro/sdk/core';
import { deploymentLease } from '~/lib/bootstrap';
import { datafeed } from '~/lib/datafeed';
const mount = ref<HTMLElement | null>(null);
let widget: ChartSdkMount | undefined;
onMounted(async () => {
const { createTradeScriptSdk } = await import('@tradescript/pro/sdk/core');
const sdk = await createTradeScriptSdk({ lease: deploymentLease });
widget = sdk.chart.mount({
mount: mount.value!,
symbol: { ticker: 'AAPL', exchange: 'NASDAQ', type: 'stock' },
interval: '15m',
datafeed,
theme: 'dark',
});
await widget.ready();
});
onBeforeUnmount(() => widget?.destroy());
</script>
<template>
<div ref="mount" class="chart" />
</template>
<style scoped>
.chart { height: 640px; }
</style>
Use the component normally from a Nuxt page. Wrap it in <ClientOnly> only when the surrounding layout needs an explicit server-rendered fallback.
Mount standalone widgets in any framework
The authorized SDK exposes every embeddable product surface through a named framework-neutral module. You provide a DOM element and typed props; the returned owner can update the mounted surface and destroy it when the route or component unmounts.
| Experience | Module | Product guide |
|---|---|---|
| Desktop chart or multi-chart workspace | sdk.chart | Chart widget |
| Touch-first chart | sdk.mobileChart | Mobile chart |
| Market depth and trade tape | sdk.marketDepth, sdk.timeAndSales | Order flow |
| Watchlist and session status | sdk.watchlist, sdk.sessionMeta | Watchlist, Session info |
| Account and order entry | sdk.accountPanel, sdk.accountSummary, sdk.orderTicket, sdk.orderTicketLauncher | Trading |
| Options and price ladder | sdk.optionChain, sdk.optionOrderTicket, sdk.ladder | Option order ticket |
| Complete terminal or composable layout | sdk.tradingTerminal, sdk.layout | Trading, Multi-chart workspace |
| Agent activity console | sdk.agentConsole | Agents and MCP |
For example, mount a chart and a market-depth panel into two framework-owned elements:
const chartMount = sdk.chart.mount({
mount: '#chart',
symbol: 'AAPL',
interval: '15m',
datafeed,
});
const widget = await chartMount.ready();
const depthMount = sdk.marketDepth.mount({
mount: '#depth',
controller: widget.data(),
symbol: widget.chart().getSymbol(),
levels: 20,
theme: 'dark',
});
// A later framework state change updates the existing panel.
depthMount.update({ levels: 50 });
// Run both when the owning route or component unmounts.
depthMount.destroy();
chartMount.destroy();
Use the same teardown point your framework already provides:
- React or Next.js: the
useEffectcleanup function. - Vue or Nuxt:
onBeforeUnmount. - Svelte: the function returned from
onMount. - Angular:
ngOnDestroy. - Plain JavaScript: the router/view disposal callback.
React and Next.js can choose either these named mounts or the exported React components. Angular, Vue, Nuxt, Svelte, and plain JavaScript do not need a React wrapper: use the named SDK modules directly. For complete standalone-widget lifecycle examples, see Standalone Widgets by Framework.
Verify the chart
The same checkpoint applies to every framework:
- Load the route that renders the starter component. You should see the chart toolbar, price scale, and a candlestick series for
AAPLat15monceloadBarsreturns. - Navigate away from the route. The component's teardown runs
destroy(); no chart DOM, worker, or subscription should remain (no continued network activity from the feed). - Navigate back. A fresh widget mounts cleanly.
Common failure modes
- Empty container: the mount element has no real height — every starter sets an explicit height for this reason.
- Chart created twice or leaks on route change: the framework unmounted before
createTradeScriptSdk({ lease })resolved — the Svelte and Next.js starters guard this with adisposedflag; keep that pattern when an async bootstrap can outlive the component. - SSR error such as
document is not defined: the chart was constructed during server rendering — use the client-only patterns shown in the Angular, Next.js, and Nuxt tabs. - Unstyled chart: SDK styles must be registered globally (Angular
styles.css, Next.js root layout, Nuxtnuxt.config.ts), not inside a scoped component stylesheet.
Try the same API live
Open the hosted playground to change the symbol, interval, and theme against a live feed and copy the resulting core mount() snippet.
Next steps
- Hosted Playground — the same API against a live feed, with no local setup.
- Core Concepts — the ownership and readiness model behind every mount above.
- Datafeeds — replace the starter feed with your own market data.
- Standalone Widgets by Framework — the same lifecycle for depth, tape, watchlist, and trading surfaces.