Rocket ECharts Pro
Rocket is currently in alpha – available with Datastar Pro.
An ECharts integration, loaded via CDN using Rocket’s declarative import.
Explanation #
ECharts is a powerful, enterprise-grade charting library with hundreds of chart types, animations, and complex interaction patterns. Despite its complexity, Rocket allows you to create a fully-featured ECharts component in around 100 lines of code. This showcases Rocket’s ability to seamlessly integrate sophisticated JavaScript libraries while maintaining clean, declarative markup.
Component Structure #
The ECharts component accepts the following props:
option– The configuration object (JavaScript object, defaults to{ }).theme– Chart theme name (string, defaults todefault).resize-delay– Delay in milliseconds before resizing (integer, defaults to100).
Simplicity Through Rocket #
What would typically require complex initialization code, manual DOM manipulation, and event handling is reduced to:
- One
data-import-iife:echartsattribute to load the library. - Reactive effects that automatically update charts when data changes.
- Built-in cleanup handling through Rocket’s lifecycle management.
- Built-in prop validation through codec syntax.
The key features of this Rocket ECharts integration include:
- Declarative imports: ECharts is loaded via CDN with a single attribute.
- Reactive updates: Charts automatically re-render when bound data changes.
- CSS variable resolution: Chart colors can use CSS custom properties.
- Responsive design: ResizeObserver ensures charts adapt to container changes.
- Theme switching: Automatic dark/light mode support via media queries.
- Component isolation: Each component maintains isolated state.
Usage Example #
1<rocket-echarts option='{
2 "title": { "text": "Sales Data" },
3 "tooltip": { "trigger": "axis" },
4 "xAxis": {
5 "type": "category",
6 "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
7 },
8 "yAxis": { "type": "value" },
9 "series": [{
10 "data": [120, 200, 150, 80, 70, 110, 130],
11 "type": "bar"
12 }]
13}'></rocket-echarts>Rocket Component #
1<template data-rocket:rocket-echarts
2 data-props:option="js|={}"
3 data-props:theme="string|=default"
4 data-props:resize-delay="int|=100"
5 data-import-esm:echarts="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.esm.js"
6>
7 <script>
8 // Store non-serializable objects
9 let chart = null
10 let currentTheme = $$theme
11
12 // Helper to resolve CSS variables in options
13 function resolveVars(val) {
14 if (Array.isArray(val)) return val.map(resolveVars)
15 if (val && typeof val === 'object') {
16 const resolved = {}
17 for (const key in val) {
18 if (Object.hasOwn(val, key)) resolved[key] = resolveVars(val[key])
19 }
20 return resolved
21 }
22 if (typeof val === 'string' && val.includes('var(')) {
23 return val.replace(/var\((--[\w-]+)\)/g, (_, name) =>
24 getComputedStyle(el).getPropertyValue(name).trim() || `var(${name})`)
25 }
26 return val
27 }
28
29 // Initialize chart - DOM and imports are ready when setup runs
30 const chartEl = $$chartNode
31 if (!chartEl) {
32 console.error('[ECharts] Chart element not found')
33 return
34 }
35
36 // Ensure container has dimensions
37 const hostHeight = el.clientHeight
38 if (hostHeight > 0 && chartEl.clientHeight === 0) {
39 chartEl.style.height = hostHeight + 'px'
40 }
41
42 // Check if ECharts is available
43 if (typeof echarts === 'undefined') {
44 console.error('[ECharts] ECharts library not available')
45 return
46 }
47
48 // Create chart instance
49 chart = echarts.init(chartEl, currentTheme === 'default' ? null : currentTheme)
50
51 // Helper to get plain value from signal (handles Proxy objects)
52 function getPlainOption() {
53 // Deep clone to resolve all proxy values and CSS variables
54 return resolveVars(JSON.parse(JSON.stringify($$option || {})))
55 }
56
57 // Set and update chart option
58 function updateOption() {
59 const option = getPlainOption()
60 if (Object.keys(option).length) {
61 chart.setOption(option)
62 }
63 }
64
65 // Set initial option
66 updateOption()
67
68 // Update chart when option changes
69 effect(updateOption)
70
71 // Handle theme changes
72 effect(() => {
73 if ($$theme !== currentTheme) {
74 currentTheme = $$theme
75 chart.dispose()
76 chart = echarts.init(chartEl, currentTheme === 'default' ? null : currentTheme)
77 updateOption()
78 }
79 })
80
81 // Set up resize observer with debounce
82 let resizeTimeout
83 const resizeObserver = new ResizeObserver(() => {
84 clearTimeout(resizeTimeout)
85 resizeTimeout = setTimeout(() => chart?.resize(), $$resizeDelay)
86 })
87 resizeObserver.observe(chartEl)
88
89 // Handle color scheme changes
90 const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
91 const handleSchemeChange = () => {
92 if ($$theme === 'default') {
93 chart.dispose()
94 chart = echarts.init(chartEl, null)
95 updateOption()
96 }
97 }
98 mediaQuery.addEventListener('change', handleSchemeChange)
99
100 // Cleanup on disconnect
101 onCleanup(() => {
102 resizeObserver.disconnect()
103 clearTimeout(resizeTimeout)
104 mediaQuery.removeEventListener('change', handleSchemeChange)
105 chart?.dispose()
106 })
107 </script>
108 <style>
109 .chart-container {
110 display: block;
111 width: 100%;
112 height: 100%;
113 min-height: inherit;
114 position: relative;
115 /* Important for ECharts' absolute positioned canvas */
116 }
117 </style>
118
119 <div class="chart-container" data-ref="chartNode"></div>
120</template>