Lifecycle and frameworks
Create the chart when its container exists, use it after it is ready, and remove it with its datafeed, in plain JavaScript, Vue, Nuxt or React.
Each widget belongs to the view that hosts it. Create it once, wait until it's ready, and remove it when the view goes away.
Two "ready" moments
onChartReady(callback) tells you the chart API is ready to use. headerReady() tells you when you can add header controls. A ready chart may still be waiting for data from the network. To know when bars have arrived, use the chart's data status or subscriptions.
widget.onChartReady(() => {
const chart = widget.activeChart();
const owner = {};
const handle = chart.onDataLoaded();
const onLoaded = () => console.log('Bars available');
handle.subscribe(owner, onLoaded);
// Before disposal: handle.unsubscribe(owner, onLoaded);
});Avoid charts that appear too late
If you load the runtime asynchronously, make sure the host view is still on screen before you create the widget. Say the user changes route while the script is still downloading. When the download finishes, it shouldn't create a chart that nobody can see. Keep one generation counter or cancellation token for each mount.
Clean up everything you started
Call widget.remove(), then close your datafeed's subscriptions, timers and network requests. Remove your own subscriptions and resize observers too. Before you retry after a failure, remove the failed widget first. Removing the visible iframe doesn't stop timers that your own code started.
When you load a saved layout, let the chart finish loading it before you apply separate history checkpoints. An undo stack is no substitute for real saving.
Framework integrations
The runtime works with any framework. Each host component should own exactly one widget and its datafeed, from creation to cleanup.
| Host | Create the chart in | Clean up in |
|---|---|---|
| Plain HTML/JS | After the document loads and the container exists | Remove the widget and stop the datafeed |
| Vue | onMounted | onBeforeUnmount |
| Nuxt | A client-only component, once mounted | The same Vue cleanup |
| React | An effect, using a DOM ref | The effect's cleanup, including the extra remount in development |
Only load runtime scripts in the browser. Type-only imports are safe at build time. Importing browser code during server-side rendering can fail because window or document doesn't exist there.
React
Create the widget once the container has mounted, and remove it in the same effect's cleanup. In development, React may mount, clean up and mount again. Make sure the first load, if it's still pending, doesn't create a chart after cleanup has run.
import { useEffect, useRef } from 'react';
import { loadChartLibrary } from './chart-loader';
import { createDemoDatafeed } from './sample-datafeed';
export function Chart() {
const container = useRef<HTMLDivElement>(null);
useEffect(() => {
let cancelled = false;
let widget: { remove(): void } | undefined;
const feed = createDemoDatafeed();
loadChartLibrary('/lumen_charting_library/').then(library => {
if (cancelled || !container.current) return;
widget = new library.widget({
container: container.current, library_path: '/lumen_charting_library/',
datafeed: feed.datafeed, symbol: 'AAPL', interval: '1D',
locale: 'en', autosize: true,
});
}).catch(console.error);
return () => { cancelled = true; widget?.remove(); feed.dispose(); };
}, []);
return <div ref={container} style={{ height: 560 }} />;
}In a real component, replace console.error with an error state and a retry option. You write the loader and sample datafeed yourself as part of your integration; they don't come from React. For chart commands that change often, call them on the existing widget. Don't add them all to the mount effect's dependencies, or the chart will be rebuilt each time.
Vue and Nuxt
Create the chart in a client-side lifecycle hook. If loading is asynchronous, check that the component is still mounted before creating the widget.
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { loadChartLibrary } from './chart-loader';
import { createDemoDatafeed } from './sample-datafeed';
const container = ref<HTMLElement>();
let cancelled = false;
let widget: { remove(): void } | undefined;
const feed = createDemoDatafeed();
onMounted(async () => {
const library = await loadChartLibrary('/lumen_charting_library/');
if (cancelled || !container.value) return;
widget = new library.widget({
container: container.value, library_path: '/lumen_charting_library/',
datafeed: feed.datafeed, symbol: 'AAPL', interval: '1D',
locale: 'en', autosize: true,
});
});
onBeforeUnmount(() => {
cancelled = true;
widget?.remove();
feed.dispose();
});
</script>
<template><div ref="container" style="height:560px" /></template>The loader and datafeed are helpers you write as part of your integration. They aren't Vue APIs, and they aren't library source you can download. Use your authorized copy of the runtime and build these helpers in your app. If the loader fails, show the error in your UI and offer a way to retry.
Nuxt
Wrap the host in <ClientOnly> or use a client component, and keep browser imports on the client side. Serve the runtime files from your app's configured asset path, and remember any deployment prefix. Avoid route watchers that rebuild the chart every time some unrelated state changes.