st.echarts_chart
Display an interactive Apache ECharts chart.
Apache ECharts is a powerful, open-source charting library with a large catalog of chart types (including gauges, sunbursts, sankey, graph/network, candlestick, radar, and more).
To show an ECharts chart in Streamlit, pass an ECharts option object as a Python dictionary (the same JSON you would pass to setOption in JavaScript), a JSON string, or a pyecharts chart instance.
To chart a dataframe, pass it as dataset.source instead of converting it to lists. Always do this for more than about 1,000 rows. For the advantages, see the spec parameter.
Note
st.echarts_chart supports only JSON-compatible option objects. Embedding JavaScript callbacks (e.g. formatter functions or renderItem) is not supported. Use ECharts' string templates instead (e.g. "formatter": "{b}: {c}"). To format tooltip values, see the tooltip note below.
Consequently, three families of ECharts charts are unavailable and raise an error: custom series (which require a renderItem callback), map and geo charts (which require registering GeoJSON map data), and 3D or WebGL charts from the ECharts GL extension (bar3D, scatter3D, globe, and similar).
Note
Strings in the option object (title.text, legend names, labels) are rendered by ECharts as plain text. Streamlit markdown does not apply inside spec. Put formatted copy in st.markdown next to the chart instead.
Note
Streamlit extends two tooltip options so you can format tooltips without JavaScript. Both work in tooltip and series[i].tooltip, but not in per-item tooltips inside series[i].data:
- valueFormatter accepts a number format, which formats each value in the default tooltip: a named format ("plain", "localized", "dollar", "euro", "yen", "percent", "compact", "scientific", "engineering", "accounting", or "bytes") or a printf-style string (e.g. "%,d sessions"), as in st.metric. "percent" multiplies values by 100. One format applies to every value of a series, so use encode.tooltip to choose which dimensions the tooltip shows. For a dataframe source, the tooltip shows each of those dimensions on its own line, labeled with its column name. Any other value, including a malformed printf-style string, is ignored, and the tooltip keeps its default formatting.
- String formatter templates in tooltips expand the {@dimension} and {@[index]} placeholders, which ECharts otherwise supports only in series labels (e.g. "{b}: {@[2]} sessions"). In an axis tooltip, they read the first series. Values are inserted as escaped text without a number format, while the template itself is HTML, as in any other ECharts tooltip string.
Note
To keep large charts fast, Streamlit fills in two ECharts options that your spec doesn't set, counting the items in each series' data or its dataset's source:
- "large": True on scatter series with more than 20,000 points and bar series with more than 2,000 bars. ECharts then draws each series as a few batched paths, which loads much faster. Tooltips still work, but hovering doesn't highlight an item and labels aren't drawn. ECharts skips animations at these sizes anyway.
- "showSymbol": False on line series with more than 2,000 points on a value or time axis, where symbols would merge into a solid band. An axis tooltip ("trigger": "axis") still marks the hovered point.
Neither applies to charts with selections (on_select, selectedMode, or a brush) or to series whose data items are objects with their own settings. Large mode also skips charts with a visualMap and series with "colorBy": "data", and line series keep their symbols with an item tooltip or empty values. To opt out, set the option yourself, for example "large": False. pyecharts charts always set both, so pass is_large=True or is_symbol_show=False yourself. For even denser lines, "sampling": "lttb" draws a subset of points that keeps the line's shape. Streamlit doesn't set it because it changes which points are drawn and shown in tooltips.
| Function signature[source] | |
|---|---|
st.echarts_chart(spec, *, width="stretch", height="content", theme="streamlit", key=None, on_select="ignore", renderer="canvas", alt=None) | |
| Parameters | |
spec (dict, str, or pyecharts chart) | The ECharts option object to render. This can be one of the following:
Put chart data in dataset.source as a dataframe-like object (pandas, Polars, PyArrow, and others) rather than as lists in series.data or xAxis.data. Always do this for more than about 1,000 rows. Graph, sankey, tree, treemap, sunburst, and other series that don't read dataset take their data in series.data instead. A dataframe source has these advantages:
Streamlit keeps the dataframe's column order through dataset.dimensions when you haven't set it. The dataframe index isn't included, so use df.reset_index() to plot it. On a category axis, ECharts reads numbers as 0-based category positions, so a marker at {"xAxis": 2023} doesn't mark the 2023 category. Pass the label as a string, and cast numeric category columns, such as years, with .astype(str). |
width ("stretch", "content", or int) | The width of the chart element. This can be one of the following:
|
height ("content", "stretch", or int) | The height of the chart element. This can be one of the following:
|
theme ("streamlit" or None) | The theme of the chart. If theme is "streamlit" (default), Streamlit applies its own colors, fonts, and plot layout. If theme is None, Streamlit leaves your spec's styling untouched and uses ECharts' built-in default theme. Two defaults still apply: aria.enabled stays on unless you set aria, so the chart keeps a screen-reader description, and a display-only chart (on_select="ignore") with no series.cursor gets the "default" cursor so it doesn't look clickable. The "streamlit" theme can be partially customized through the configuration options theme.chartCategoricalColors and theme.chartSequentialColors. Font configuration options are also applied, and long tooltip numbers are shortened unless you set tooltip.valueFormatter or tooltip.formatter (see the tooltip note above). Time, value, and log axes hide tick labels that would overlap. To show every label on an axis, add "hideOverlap": False to its axisLabel. On a time xAxis or yAxis, the axis tooltip header and axis pointer label show a midnight value as a date (for example 2026-08-18) and any other value as a date and time to the second, so an hourly point at midnight shows only its date. An axisPointer.label.formatter replaces this, whether it's set on the axis, tooltip, grid.tooltip, the top-level axisPointer, a timeline tick, or a media override. "{value}" restores ECharts' default label. Tooltip values show the datetimes of a dataframe source the same way, in the browser's local time even with "useUTC": True, so a tooltip datetime can differ from its axis label. |
key (str, int, or None) | An optional key that gives this element a stable identity. If this is None (default) and the chart is display-only, identity follows its position in the app, so moving it can reset the chart and replay its entry animation. If this is None and selections are activated, identity also includes the chart's parameters, so a change to the spec, theme, renderer, size, or alt resets the selection. If selections are activated and key is provided, Streamlit will register the key in Session State to store the selection state, and the selection survives changes to your spec, theme, and renderer. Indices are not revalidated against the new data, so check them before indexing. The selection state is read-only. For more details, see Widget behavior. If key is provided, it will be used as a CSS class name prefixed with st-key-, and the chart keeps its identity across reruns even when the spec, theme, or renderer changes. |
on_select ("ignore", "rerun", or callable) | How the chart should respond to user selection events. This controls whether or not the chart behaves like an input widget. on_select can be one of the following:
When on_select is not "ignore", Streamlit returns whatever selections you enable in your spec. Enable point selection by setting selectedMode on a series (for example, {"type": "bar", "selectedMode": "multiple", "data": [...]}), and enable region selection by adding a brush component or toolbox.feature.brush. Target an axis on the brush (for example, "brush": {"xAxisIndex": "all"}) so its regions are reported in areas and survive resizes. A brush without an axis, grid, or geo target only has pixel geometry, so its regions are omitted from areas and dropped when the chart resizes. Selected data is grouped by series in EChartsState.selection.selected, and brush geometry is returned in EChartsState.selection.areas. Selections are re-applied visually after reruns. Double-click the chart to clear both point and brush selections. If your spec enables neither, the chart still renders but never returns a selection, and Streamlit logs a warning. |
renderer ("canvas" or "svg") | The renderer passed to ECharts. This can be one of the following:
|
alt (str or None) | A description of the chart for screen readers and other assistive technologies. Streamlit maps this to ECharts' aria.label.description, which becomes the chart's accessible name. If this is None (default), ECharts keeps its generated data-derived name when aria.enabled is on. An empty or whitespace-only string is treated the same as None and is logged so authors notice the dual meaning of alt="" across commands (decorative only on st.image / st.pyplot). When both alt and an author aria.label.description are set, alt overrides that description for the accessible name. A non-empty alt also keeps the chart named even if the option sets aria.enabled to False. Because ECharts already generates a data-derived name, prefer a short, specific description; a vague one can be worse than none. This is a short description of the chart, not a full text alternative for dense graphics. |
| Returns | |
(element or EChartsState) | If on_select is "ignore" (default), this command returns an internal placeholder for the chart element. Otherwise, this command returns an EChartsState object. This object is dictionary-like and supports both key and attribute notation. To use this type in an annotation, import it from streamlit.typing. |
Examples
Example 1: Basic bar chart
import streamlit as st st.echarts_chart( { "xAxis": {"type": "category", "data": ["A", "B", "C", "D", "E"]}, "yAxis": {"type": "value"}, "series": [{"type": "bar", "data": [5, 20, 36, 10, 10]}], }, alt="Bar chart of categories A through E", )
Example 2: Chart from a dataframe
Pass a dataframe as dataset.source. Streamlit preserves its column order through dataset.dimensions, so ECharts maps the first column to the category axis and each remaining column to a series. Hover the chart and choose Show data to view the dataframe in a table.
import pandas as pd import streamlit as st df = pd.DataFrame( { "product": ["Matcha", "Milk Tea", "Cocoa"], "2015": [43.3, 83.1, 86.4], "2016": [85.8, 73.4, 65.2], } ) st.echarts_chart( { "legend": {}, "tooltip": {}, "dataset": {"source": df}, "xAxis": {"type": "category"}, "yAxis": {}, "series": [{"type": "bar"}, {"type": "bar"}], } )
Example 3: Zoom slider, toolbox, and legend
In-chart controls such as dataZoom and toolbox are configured in the spec. ECharts' toolbox is separate from Streamlit's hover toolbar (Show data, download, and fullscreen), so set toolbox.left to keep the two from stacking in the top-right corner, and omit saveAsImage if Streamlit's download is enough. Place legend at the top so it doesn't share the footer with a bottom dataZoom slider. Each series takes its legend name from its dataframe column.
With an inside dataZoom or wheel roam (on by default for treemaps), the wheel scrolls the page until the pointer rests on the chart, then zooms or pans. A pinch, or the dataZoom modifier set in zoomOnMouseWheel or moveOnMouseWheel, acts on the first wheel of a new flick, unless that flick started as a page scroll.
import pandas as pd import streamlit as st df = pd.DataFrame( { "quarter": ["Q1", "Q2", "Q3", "Q4"], "Revenue": [820, 932, 901, 934], "Cost": [500, 610, 550, 700], } ) st.echarts_chart( { "legend": {"top": 28}, "tooltip": {"trigger": "axis"}, "toolbox": { "left": 0, "feature": { "magicType": {"type": ["line", "bar"]}, "restore": {}, }, }, "dataZoom": [{"type": "inside"}, {"type": "slider"}], "dataset": {"source": df}, "xAxis": {"type": "category"}, "yAxis": {"type": "value"}, "series": [{"type": "line"}, {"type": "bar"}], } )
Example 4: Point selections driving the app
Set on_select="rerun" to make the chart behave like an input widget, and enable point selection in your spec by setting selectedMode on the series. Streamlit returns selected indices grouped by series and data type. For a dataset without transforms, the indices address the dataframe's rows.
import pandas as pd import streamlit as st df = pd.DataFrame( { "day": ["Mon", "Tue", "Wed", "Thu", "Fri"], "sales": [120, 200, 150, 80, 70], } ) event = st.echarts_chart( { "dataset": {"source": df}, "xAxis": {"type": "category"}, "yAxis": {"type": "value"}, "series": [{"type": "bar", "selectedMode": "multiple"}], }, key="sales", on_select="rerun", ) rows = [ i for group in event.selection.selected for i in group["data_indices"] ] st.dataframe(df.iloc[rows])
Chart selections
EChartsState
The schema for the ECharts chart event state.
To use this type in an annotation, import it from streamlit.typing.
The event state is stored in a read-only dictionary-like object that supports both key and attribute notation. Event states cannot be programmatically changed or set through Session State.
Only selection events are supported at this time.
selection is the Streamlit-normalized payload described by EChartsSelectionState, not a raw ECharts event.
| Attributes | |
selection (dict) | The state of the on_select event. This attribute returns a dictionary-like object that supports both key and attribute notation. The attributes are described by the EChartsSelectionState dictionary schema. |
Example
The event state wraps that selection payload:
{
"selection": {
"selected": [
{
"series_index": 0,
"series_id": "sales",
"series_name": "Sales",
"data_type": "main",
"data_indices": [1, 2, 3],
},
],
"areas": [
{
"brush_index": 0,
"brush_type": "rect",
"coord_range": [[0, 2], [10, 20]],
},
],
}
}
EChartsSelectionState
The schema for the ECharts chart selection state.
The selection state is stored in a read-only dictionary-like object that supports both key and attribute notation. Selection states cannot be programmatically changed or set through Session State.
This is a Streamlit-normalized snapshot of ECharts selection events, not the raw selectchanged or brushSelected payload. Exposed field names use snake_case; ECharts string values, such as brush_type="lineX", remain unchanged.
Empty series groups that ECharts emits are omitted, so selected[0] is the first series that has a selection, not necessarily series 0. Iterate the groups and match series_index. A brush region can appear in areas even when it hits no data (selected is empty).
| Attributes | |
selected (list[dict[str, Any]]) | The union of native and brushed selections, grouped by ECharts series and data type. Each entry contains series_index, series_id, series_name, data_type, and data_indices. Series IDs and names are None when they weren't explicitly configured. data_type is "main" (used when ECharts omits dataType), "node", or "edge". data_indices is sorted and series-local. For a dataset without transforms, it addresses dataset.source rows. For inline series, it addresses series.data. For graph series, data_type="node" addresses data and data_type="edge" addresses links. |
areas (list[dict[str, Any]]) | The active brush regions. Each entry contains brush_index, brush_type, and coord_range. brush_type is ECharts' "rect", "lineX", "lineY", or "polygon". The shape of coord_range depends on that type: "rect" uses [[x0, x1], [y0, y1]]; "lineX" and "lineY" use [start, end]; "polygon" uses [[x, y], ...]. Category-axis coordinates are ordinal positions and can include half-indices such as [0.5, 3.5]. Regions that ECharts only describes in pixel space are omitted, since their geometry can't be mapped back to your data. |
Example
Selecting points and a rect brush returns a grouped, series-local payload:
{
"selected": [
{
"series_index": 0,
"series_id": "sales",
"series_name": "Sales",
"data_type": "main",
"data_indices": [1, 2, 3],
},
],
"areas": [
{
"brush_index": 0,
"brush_type": "rect",
"coord_range": [[0, 2], [10, 20]],
},
],
}
If event is the return value of st.echarts_chart and df holds the data of a single series, filter it with:
rows = (
event.selection.selected[0]["data_indices"]
if event.selection.selected
else []
)
filtered_df = df.iloc[rows]
Tip
Want a new st.echarts_chart feature or found a bug? Browse open issues and react with a ๐ on the initial post of the ones that matter to you. Your votes help us prioritize what to work on next.
Still have questions?
Our forums are full of helpful information and Streamlit experts.