# ECharts integration
`markstream-vue` supports rendering ECharts charts through custom components. This page covers three recommended approaches, from simplest to most advanced.
## Why ECharts with markstream-vue?
ECharts is a powerful charting library that works naturally with the streaming architecture of `markstream-vue`. You can:
- Write chart options directly in Markdown code blocks
- Stream chart updates progressively (ideal for AI-generated content)
- Reuse existing ECharts themes and configurations
- Export charts as images
## Approach 1: Custom CodeBlock component (Recommended)
This approach treats ECharts as a special code block language, similar to how Mermaid is handled. It's the most user-friendly and follows the existing architecture pattern.
### 1.1 Create the ECharts component
Create `EChartsBlockNode.vue`:
```vue
```
### 1.2 Register the custom component
```ts
// main.ts or your component entry
import { setCustomComponents } from 'markstream-vue'
import EChartsBlockNode from './components/EChartsBlockNode.vue'
setCustomComponents({
code_block: EChartsBlockNode
})
```
### 1.3 Use in Markdown
Now you can write ECharts charts directly in Markdown:
```markdown
\```echarts
{
"title": { "text": "Sales Data" },
"tooltip": {},
"xAxis": {
"type": "category",
"data": ["Mon", "Tue", "Wed", "Thu", "Fri"]
},
"yAxis": { "type": "value" },
"series": [{
"type": "bar",
"data": [120, 200, 150, 80, 70]
}]
}
\```
```
### 1.4 Install ECharts
```bash
pnpm add echarts
# or
npm install echarts
```
### Pros & Cons
**Pros:**
- Familiar code block syntax
- Follows existing Mermaid pattern
- Works with streaming updates
- Easy to implement
**Cons:**
- Overrides all `code_block` rendering (need to handle non-echarts blocks)
- Requires JSON parsing (no JS expressions)
---
## Approach 2: Custom HTML tag
Use a custom HTML-like tag for ECharts charts, providing clearer separation from regular code blocks.
### 2.1 Configure custom HTML tags
```ts
// parser configuration (or pass `custom-html-tags` to MarkdownRender)
import { getMarkdown } from 'markstream-vue'
const md = getMarkdown('echarts', {
customHtmlTags: ['echarts', 'chart'],
})
```
### 2.2 Register the component
```ts
import { setCustomComponents } from 'markstream-vue'
import EChartsComponent from './components/EChartsComponent.vue'
setCustomComponents({
echarts: EChartsComponent,
chart: EChartsComponent
})
```
### 2.3 Create the component
```vue
```
### 2.4 Use in Markdown
```markdown
{
"series": [{
"type": "pie",
"data": [
{ "value": 335, "name": "Direct" },
{ "value": 310, "name": "Email" },
{ "value": 234, "name": "Affiliate" }
]
}]
}
```
### Pros & Cons
**Pros:**
- Clear type separation
- Supports custom attributes (width, height)
- Doesn't interfere with code blocks
**Cons:**
- Requires parser configuration
- Less familiar syntax
- Not standard Markdown
---
## Approach 3: VmrContainer integration
Use ECharts inside a custom container (similar to admonitions), ideal for documentation with chart descriptions.
### 3.1 Register container component
```ts
import { setCustomComponents } from 'markstream-vue'
import EChartsContainerNode from './components/EChartsContainerNode.vue'
setCustomComponents({
vmr_container: EChartsContainerNode
})
```
### 3.2 Create the container component
```vue
```
### 3.3 Use in Markdown
```markdown
:::echarts
{
"title": { "text": "Traffic Sources" },
"series": [{
"type": "pie",
"radius": "50%",
"data": [
{ "value": 1048, "name": "Search" },
{ "value": 735, "name": "Direct" },
{ "value": 580, "name": "Marketing" }
]
}]
}
:::
```
### Pros & Cons
**Pros:**
- Consistent with existing container system
- Supports additional content around charts
- Good for documentation
**Cons:**
- More complex setup
- Requires parsing children
---
## SSR Considerations
ECharts requires the DOM and doesn't support server-side rendering. Use one of these strategies:
### For Nuxt 3
```vue
```
### For SSR with fallback
```vue
Loading chart...
```
### Check for window in component
```ts
import { computed } from 'vue'
const canRender = computed(() => typeof window !== 'undefined' && isECharts.value)
```
---
## Advanced Features
### ResizeObserver for responsive charts
```ts
import { useResizeObserver } from '@vueuse/core'
useResizeObserver(chartRef, (entries) => {
if (chartInstance && entries[0]) {
const { width, height } = entries[0].contentRect
if (width > 0 && height > 0) {
chartInstance.resize()
}
}
})
```
### Export as image
```ts
function exportChart() {
if (!chartInstance)
return
const url = chartInstance.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: props.isDark ? '#1a1a1a' : '#fff'
})
const link = document.createElement('a')
link.href = url
link.download = 'chart.png'
link.click()
}
```
### Theme switching with ECharts themes
```ts
import dark from 'echarts/theme/dark'
import { computed } from 'vue'
const theme = computed(() => props.isDark ? 'dark' : undefined)
chartInstance = echarts.init(chartRef.value, theme.value)
```
---
## Which approach should I choose?
| Approach | Best for | Complexity |
|----------|----------|------------|
| **CodeBlock** | Most users, simple integration | ⭐ |
| **Custom HTML tag** | Advanced users, multiple chart types | ⭐⭐ |
| **VmrContainer** | Documentation, styled containers | ⭐⭐⭐ |
**Recommendation:** Start with **Approach 1 (CodeBlock)**. It's the most straightforward and aligns with how users already interact with Mermaid diagrams.
---
## Complete example
Here's a complete minimal setup using Approach 1:
```ts
import MarkdownRender, { setCustomComponents } from 'markstream-vue'
// main.ts
import { createApp } from 'vue'
import EChartsBlockNode from './EChartsBlockNode.vue'
import 'markstream-vue/index.css'
import 'echarts'
// Register custom component
setCustomComponents({
code_block: EChartsBlockNode
})
const app = createApp({
components: { MarkdownRender },
template: `
`,
data() {
return {
markdown: `
\`\`\`echarts
{
"xAxis": { "type": "category", "data": ["A", "B", "C"] },
"yAxis": { "type": "value" },
"series": [{ "type": "bar", "data": [10, 20, 30] }]
}
\`\`\``
}
}
})
app.mount('#app')
```
---
## Troubleshooting
1. **Chart not rendering** — Check browser console for JSON parse errors. Ensure the code block content is valid JSON.
2. **Blank container** — Verify ECharts is installed and the DOM element has dimensions. Set explicit width/height styles.
3. **Theme not switching** — ECharts instances need to be re-created when changing themes. Use `watch(() => props.isDark)` to dispose and reinitialize.
4. **SSR errors** — Wrap the component in `` (Nuxt) or check `typeof window !== 'undefined'` before initializing.
5. **Memory leaks** — Always call `chartInstance.dispose()` in `onBeforeUnmount`.
Still having issues? Check the [ECharts documentation](https://echarts.apache.org/en/index.html) or open an issue on GitHub.