# streamlit-echarts: putting Apache ECharts inside a Streamlit app

> streamlit-echarts is a Streamlit component that renders Apache ECharts from a plain option dict, with chart events wired back into Python, selection handling like st.plotly_chart, and a PyECharts convenience wrapper. It suits a Python dashboard author who wants ECharts specifically, and it is a best effort project that asks you to fork rather than wait for a native implementation.

**andfanilo/streamlit-echarts** — A Streamlit component to render ECharts.

- Repository: https://github.com/andfanilo/streamlit-echarts
- Website: https://echarts.streamlit.app/
- Stars: 638 · Forks: 88
- Language: Python
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/andfanilo-streamlit-echarts

## A component that renders ECharts options verbatim

streamlit-echarts is a Streamlit component to display ECharts, and the homepage is a live demo at echarts.streamlit.app. The component name on PyPI is streamlit-echarts, the primary language is Python, and the licence is MIT.

The core contract is simple. You hand it the ECharts option object as a dict, it renders the chart, and nothing is translated in between.

```python
import streamlit as st
from streamlit_echarts import st_echarts

options = {
    "xAxis": {
        "type": "category",
        "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    },
    "yAxis": {"type": "value"},
    "series": [{"data": [820, 932, 901, 934, 1290, 1330, 1320], "type": "bar"}],
}

st_echarts(options=options, height="400px")
```

That is the whole of the basic usage, and the fact that the options dict is the ECharts option object is the design decision. ECharts is a JavaScript library with an extensive option surface, and any wrapper that exposes only a subset of it has to invent an API for the rest. Passing the option object through means the ECharts documentation is your documentation, and every option and every combination of options is available without the wrapper growing a new keyword for each one.

The alternative, a hand written Streamlit component that exposes a curated subset of chart types, gives a nicer Python API and stops at whatever the author implemented. This one gives you the whole library and leaves the layout work to you.

The last push was on 2026-10-16, the default branch is develop, and the releases are v0.7.0 from 2026-06-08, v0.6.1 from 2026-06-02 and v0.6.0 from 2026-03-04.

## Chart events that come back to Python as data

The feature that separates this from a static chart embed is the events mechanism, and it is where the component earns its keep.

The events parameter is a dict mapping ECharts event names to JavaScript handler strings, or to paths to a .js file. Handlers run with chart and echarts in scope, and the API reference notes that a name prefixed with zr: listens across the whole canvas, including blank areas. A handler's return value surfaces in Python as result.chart_event, and returning undefined skips the rerun.

That last detail is the one that makes this usable in a Streamlit app. Streamlit reruns the whole script on every interaction, so a chart that fires an event on every mouse move would be unusable if each event triggered a rerun. Returning undefined means the handler did something without asking Streamlit to do anything, and returning a value means the event has become a piece of Python data that the app can act on.

The JsCode helper exists to make the handlers safer to write. It wraps a JavaScript string so the frontend evaluates it as a live function rather than a plain string, and it is meant to be used wherever ECharts expects a callback, such as formatters, symbol sizes and color functions. It also accepts a pathlib.Path, or a single-line .js or .mjs path string, to load the function from a local file, which the README describes as handy for longer callbacks you want to lint, format and test as real JavaScript.

```python
from pathlib import Path

JsCode("function(params){ return params.value * 2 }")
JsCode(Path("callbacks/label_formatter.js"))  # file holds one function expression
```

The demo app is where the advanced examples live, with chart-scope helpers, the zr: prefix and loading handlers from a file.

## Selection handling that mirrors st.plotly_chart

Streamlit's own plotting components grew a structured selection API, and streamlit-echarts follows the same shape, which means a developer who has used one will recognise the other.

The on_select parameter takes "ignore", which is the default, or "rerun", which triggers a Streamlit rerun, or a callable which is invoked on a selection change. The selection_mode parameter takes "points" for click, "box" for a rectangular brush and "lasso" for a polygon brush, or a tuple of several modes, and it defaults to all three.

The pattern in use looks like this.

```python
result = st_echarts(options=options, on_select="rerun", selection_mode="points", key="my_chart")

selected = result["selection"]
if selected["point_indices"]:
    st.write("Selected indices:", selected["point_indices"])
```

The result is a dict, and the selection key holds the indices of the selected points, which is the part that matters for an application. Indices rather than the data itself, which means the mapping back to your dataframe rows is explicit and under your control.

There is a related parameter worth knowing about in the same table, the key parameter, a stable widget key that prevents remount and animation replay on rerun. That is not a cosmetic detail. A Streamlit rerun re-renders the component, and without a stable key an animated chart will restart its animation on every interaction, which in a dashboard with transitions is the kind of small wrongness that makes a demo feel broken. The key is what tells Streamlit that this is the same chart it was rendering a moment ago.

The renderer parameter is the other one worth a sentence. It takes "canvas", the default, or "svg", and the reference table notes that svg is better for print and accessibility.

## PyECharts charts, and a path that does not need the extra

Not everyone writes ECharts options by hand, and the component handles the PyECharts library as well.

The st_pyecharts function is a convenience wrapper that converts a PyECharts chart instance to a dict and calls st_echarts. It requires the pyecharts extra, accepts the same parameters as st_echarts with chart replacing options, and under the hood it calls chart.dump_options() and passes the result to st_echarts.

```python
from pyecharts import options as opts
from pyecharts.charts import Bar
from streamlit_echarts import st_pyecharts

b = (
    Bar()
    .add_xaxis(["Microsoft", "Amazon", "IBM", "Oracle", "Google", "Alibaba"])
    .add_yaxis(
        "2017-2018 Revenue in (billion $)", [21.2, 20.4, 10.3, 6.08, 4, 2.2]
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="Top cloud providers 2018", subtitle="2017-2018 Revenue"
        )
    )
)

st_pyecharts(b, height="500px")
```

The extra is installed with a single command.

```sh
uv pip install streamlit-echarts[pyecharts]
```

And the README is careful to point out that the extra is not strictly required. You can convert PyECharts options manually without installing it.

```python
import json
from streamlit_echarts import st_echarts

st_echarts(options=json.loads(b.dump_options()), height="500px")
```

That distinction matters more than it looks. The extra pulls in the whole PyECharts library, which is a large dependency with its own opinions about rendering, and the wrapper here does one thing with it, dump the options and hand them over. A team that already uses PyECharts elsewhere in a non Streamlit context can therefore use this component without adding a second copy of the library to the Streamlit environment.

The optional pyecharts extra is declared in the project metadata as pyecharts>=2.0, and the runtime dependency is a single entry, streamlit >= 1.53, which is a floor high enough to assume the modern component API.

## A best effort project that asks you to fork

The first paragraph of the README is the one a potential adopter should read before anything else, and it is unusually frank about the project's status.

It describes the project as best effort, says that every now and then the author will add something they need themselves and let a coding agent do most of the typing, and says plainly that they do not have the time to go through bigger issues or pull requests. For a larger feature you want, the instruction is to fork away.

The next paragraph explains why the project exists in this form. There is a request to add a thumbs up to a Streamlit issue if you wish to see a native implementation maintained by the Streamlit team.

So the honest summary is that this is one developer's component, built on their own schedule, doing the work of bridging ECharts into Streamlit, and the author has made their position on support explicit rather than leaving it to be discovered.

That framing changes how you should read the rest of the repository, and the repository contents back it up. There is a justfile that sets a Windows shell of pwsh.exe, a setup recipe that installs Python and frontend dependencies and runs pre-commit install, separate dev recipes for the frontend with npm i --legacy-peer-deps, and a dev recipe using the Vite watch rebuild. The frontend is a separate npm project inside the Python package, with its own test runner and its own formatting check, which means this is a JavaScript component with a Python wrapper rather than a Python library that happens to call a browser.

The test story is unusually complete for a component of this kind. There are unit test recipes for Python and for the frontend, an end-to-end group that installs Playwright and its browsers, a Playwright end-to-end recipe that runs with pytest-xdist, and a separate e2e dependency group with pixelmatch and Pillow, which is how you assert that a chart rendered correctly rather than merely that the page loaded.

## Where it stops, and what to reach for instead

Three limits are worth naming, and none of them is a defect in the component.

The first is the maintenance one, already described. A component whose author says they will not review pull requests is a component you fork, and the cost of forking is that you now own the frontend as well as the Python wrapper, including the JavaScript dependency tree and the browser compatibility questions that come with it.

The second is that this is a Streamlit component and not a framework integration. It renders a chart in a Streamlit app. It is not a plotting library, it is not a dashboard framework, and it does not replace Streamlit's own chart elements. If what you need is a bar chart that updates with a dataframe, Streamlit's built in components and the Altair based alternatives cover that, and the reason to reach for ECharts is specific: a chart type or an interaction that the built in elements do not offer, such as the brush and lasso selection modes, the event handlers, or a particular ECharts series type.

The third is the ECharts dependency itself. The rendering happens in the browser with the ECharts JavaScript library, and the frontend build in the repository carries its own node_modules with the legacy peer deps flag in the setup recipe, which is the usual sign of a dependency tree that has not been modernised. The component therefore inherits ECharts' own maintenance and licensing situation, and a project with a policy about JavaScript dependencies has to account for that as well as for the Python side.

For a team that wants interactive charting with a first party component and no browser side JavaScript to own, and for a project where the chart choice is not worth arguing about, the built in Streamlit elements are the better answer. For a team that has already chosen ECharts for its other front ends and wants the same look in a Streamlit dashboard, this component is the bridge, and the README's own request for a native implementation is the clearest statement of what the author would prefer to exist.

## Conclusion

streamlit-echarts fits a Streamlit author who has committed to ECharts as the charting library and needs the full option set plus interactive selection wired back into Python, and it does not fit a team that wants a maintained first party component or a chart library with a native Streamlit path already. Before adopting it, read the events documentation in the live demo app, because the JavaScript handler mechanism is the part of the API that needs the most understanding, and check the 0.7.0 release notes, since the project is marked best effort and its own author says to fork for larger features.

## FAQ

### How do I install streamlit-echarts?

With uv pip install streamlit-echarts, and Python 3.10 or later is required. To also use PyECharts charts with st_pyecharts, install the extra with uv pip install streamlit-echarts[pyecharts]. The package is also on PyPI as streamlit-echarts under the MIT licence.

### How do chart events work in streamlit-echarts?

The events parameter maps ECharts event names to JavaScript handler strings or to paths to a .js file. Handlers run with chart and echarts in scope, a name prefixed with zr: listens across the whole canvas, and the handler's return value surfaces in Python as result.chart_event, with returning undefined skipping the rerun.

### Does streamlit-echarts support selecting points on a chart?

Yes. The on_select parameter takes ignore, rerun or a callable, and selection_mode takes points for click, box for a rectangular brush and lasso for a polygon brush, or a tuple of several. The result dict carries the selection under result["selection"], including point_indices.

### Do I have to install PyECharts to use st_pyecharts?

The extra is a convenience rather than a requirement. The README shows that you can convert PyECharts options yourself with json.loads(b.dump_options()) and pass that to st_echarts without installing the extra, because the wrapper's only job is to call dump_options and hand the result on.

## Sources

- [andfanilo/streamlit-echarts on GitHub](https://github.com/andfanilo/streamlit-echarts)
- [License: MIT](https://github.com/andfanilo/streamlit-echarts/blob/develop/LICENSE)
- [Project website](https://echarts.streamlit.app/)
- [README](https://github.com/andfanilo/streamlit-echarts/blob/develop/README.md)
- [Releases](https://github.com/andfanilo/streamlit-echarts/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/andfanilo-streamlit-echarts
