vizly
Fully themable, low-boilerplate charting over Apache ECharts.
Ship production charts in a few lines of Python (DataFrame in, HTML or JSON out) without nested option builders. Built for speed (local assets, one ECharts load per page) and for native embeds in the stacks you already use.
- Easy to use: set a theme, call
vz.line/vz.bar/ …, export withto_html()orto_option() - Highly performant: vendored JS by default; GL and plugins load only when a chart needs them
- Worldwide maps by default: bundled world atlas plus
usa; register extra GeoJSON packs when you need them - Trusted asset defaults: no Chinese CDN forced in the backend (unlike many other Python ECharts wrappers)
- Wide chart coverage: cartesian, statistical, geo, graph, 3D, compose (
page/tab/timeline), and more. See Chart inventory. - Native integrations: Streamlit, FastAPI, Flask, Django, HTMX, and Jupyter
import vizly as vz
vz.map(df) # world (default)
vz.map(df, map="usa") # US states
vz.register_map_pack(...) # custom GeoJSON you supply
Install
Requires Python 3.9 or later.
pip install vizly
Framework extras (install only what you use): vizly[streamlit], vizly[fastapi], vizly[flask], vizly[django].
HTMX helpers (vizly.integrations.htmx) ship in the base package. No vizly[htmx] extra.
30-second example
import pandas as pd
import vizly as vz
vz.set_theme("corporate")
df = pd.DataFrame({"date": ["2026-01-01", "2026-01-02"], "revenue": [10, 20]})
chart = vz.line(df, x="date", y="revenue", title="Revenue")
chart.to_html() # self-contained local ECharts
chart.to_option() # plain dict for APIs / agents
Theming
vz.set_theme("corporate")
# builtins: default, light, dark, corporate, minimal, contrast,
# ops_grafana, ops_cloudwatch, ops_kibana # ops-inspired (see below)
vz.set_theme({"palette": ["#0B1F33", "#2F6FED"]}) # deep-merge override
vz.register_theme("acme", {"background": "#FFFFFF", "palette": ["#111111"]})
vz.load_theme("examples/themes/atlantic.json", activate=True, register=True)
vz.export_theme("acme", "acme.json")
# Per-chart override does not mutate the session theme
vz.bar(df, x="region", y="sales", theme="dark")
Ops-inspired themes
ops_grafana, ops_cloudwatch, and ops_kibana are visual inspiration only: denser grids, muted animation, and step-friendly lines so charts feel at home next to common ops UIs. They are not affiliated with Grafana Labs, Amazon Web Services, or Elastic; no logos or proprietary design-system assets are shipped. Theme IDs use an ops_ prefix intentionally (zero trademark surface).
CDN asset URLs are allowlisted to cdn.jsdelivr.net and unpkg.com only. See src/vizly/assets/README.md for vendored file provenance.
Ops metric helpers
Agents/ops often receive Prometheus, CloudWatch, or Elasticsearch JSON. Shape it, then chart:
import vizly as vz
df = vz.from_prometheus(prom_api_json) # timestamp, value, series
df = vz.from_cloudwatch(cw_datapoints) # timestamp, value [, unit|series]
df = vz.from_elasticsearch(es_search_json) # timestamp, value (alias: from_elk)
vz.set_theme("ops_grafana")
vz.line(df, x="timestamp", y="value") # single series
# multi-series Prometheus matrix → group or filter by `series` column
These helpers do not call live APIs. They only normalize payloads you already have.
Trust / asset policy
| Mode | Behavior |
|---|---|
assets.mode = "local" (default) |
Inline vendored echarts.min.js (+ GL/plugins only when needed) |
assets.mode = "cdn" |
Allowlisted hosts only |
| Banned by default | China-primary CDN hosts (bootcdn, npmmirror, assets.pyecharts.org, …) |
Maps: bundled world + usa; extra regional packs via register_map_pack.
Chart inventory
vz.list_chart_types()
vz.list_unavailable_chart_types() # e.g. chord (upstream-unavailable)
line, bar, area, scatter, pie, donut, boxplot, heatmap, candlestick / kline, radar, funnel, gauge, sankey, treemap, map, grid, mix / combo, effect_scatter, waterfall, polar, parallel, sunburst, tree, graph, wordcloud, geo, bar3d, line3d, scatter3d, page, tab, timeline, pictorial_bar, theme_river, liquid, surface3d
Extra map packs are not chart types. Register them with vz.register_map_pack when you need them.
Integrations
Shared embed contract (vizly.integrations):
| Mode | Helper idea | Output |
|---|---|---|
| Full document | Streamlit / FastAPI page | <!DOCTYPE html>… |
| Fragment | Flask/Django/HTMX | div.vizly-embed + scripts |
| Dashboard | dashboard_html / vz.page / st_dashboard |
many charts, ECharts once |
| JSON | APIs / SPA | to_option() / to_json() |
Multi-chart / dashboard (important)
Single-chart helpers can include the ECharts library so one embed works alone. For several charts on one page, load assets once. Otherwise each chart ships ~1MB of JS.
from vizly.integrations import assets_html, chart_html, dashboard_html
# Pattern A: shell page (auto GL/plugins from charts=)
head = assets_html(charts=[c1, c2])
a = chart_html(c1, fragment=True, include_assets=False)
b = chart_html(c2, fragment=True, include_assets=False)
# Pattern B: one HTML blob (assets once internally)
html = dashboard_html([c1, c2], title="Ops")
# same idea: vz.page(charts=[c1, c2]).to_html()
Django:
{% load vizly_tags %}
<head>{% vizly_assets charts=charts %}</head>
{% vizly_chart c1 %}
{% vizly_chart c2 %}
{{ c3|vizly_html }}
{# or one blob: {% vizly_dashboard charts %} #}
HTMX fragments already default include_assets=False (see examples/htmx_demo).
Streamlit
Each components.html call is a separate iframe. Prefer one iframe for many charts:
from vizly.integrations.streamlit import st_vizly, st_dashboard
st_vizly(chart, height=420) # one chart
st_dashboard([c1, c2], height=900) # many charts, ECharts once
st_vizly([c1, c2], height=900) # same as st_dashboard
# examples/streamlit_app.py
FastAPI
from vizly.integrations.fastapi import html_response, json_response, dashboard_response
# examples/fastapi_app.py → / /dashboard /option
Flask
from vizly.integrations.flask import assets_html, chart_html, dashboard_response
# examples/flask_app.py → / /dashboard /dashboard/full
Django templates
INSTALLED_APPS = [..., "vizly.integrations.django"]
{% load vizly_tags %}
{% vizly_assets charts=charts %}
{% vizly_chart chart height="420px" %}
{{ chart|vizly_html }}
{# single chart without assets tag: {{ chart|vizly_html:"assets" }} #}
See examples/django_demo/.
HTMX
from vizly.integrations.htmx import htmx_chart_fragment, htmx_or_full
# examples/htmx_demo/app.py: button hx-get swaps #chart
Jupyter
Open examples/jupyter_gallery.ipynb: charts display via _repr_html_().
Less boilerplate than raw option builders
Verbose option-builder style (illustrative): many nested calls for series, axes, tooltip, and theme (~15-25 lines).
vizly after one theme call:
vz.set_theme("corporate")
vz.bar(df, x="region", y="sales", title="Sales") # ~1-2 lines
Escape hatches remain: chart.update(...), chart.merge_option({...}), vz.from_option(option).
Examples
| Path | Purpose |
|---|---|
examples/streamlit_app.py |
Streamlit (single + dashboard) |
examples/fastapi_app.py |
FastAPI HTML/JSON/dashboard |
examples/flask_app.py |
Flask + Jinja fragment / dashboard |
examples/django_demo/ |
Django {% vizly_chart %} + {% vizly_assets %} |
examples/htmx_demo/app.py |
HTMX partial swap |
examples/jupyter_gallery.ipynb |
Notebook |
examples/themes/atlantic.json |
Custom theme |
examples/band_a_gallery.py / band_bc_gallery.py |
Chart HTML gallery seeds |
Development
For work on this repository (not needed for pip install vizly):
pip install -e ".[dev,examples]"
python -m pytest -m "not browser" -v
| Level | Command | Role |
|---|---|---|
| 1+2 (CI gate) | pytest -m "not browser" |
Contracts + sample/golden HTML |
| 3 Browser | pytest -m browser |
Headless hero charts (Playwright) |
| Local visual review | python scripts/validate_samples_browser.py --gallery |
Headed Chromium + gallery |
Refresh option goldens: python scripts/update_sample_goldens.py
Full testing runbook: TESTING.md. Also CHANGELOG.md and RELEASE.md.
Known limitations
- SPA / JSON + maps:
to_option()/json_responsereturn the ECharts option only. They do not embed GeoJSON. HTML rendering callsecharts.registerMapfor you; SPA clients must register map packs themselves (or use HTML embeds). - page / tab JSON:
to_option()returns a compose descriptor under_vizly_compose(child options). Use HTML embeds (dashboard_html/to_html) for browser layout. - PNG/PDF export (deferred):
vizly[export]is not shipped yet. ECharts draws in JavaScript, so image export needs a JS canvas runtime (Node +node-canvasper Apache’s SSR guide, or a headless browser), not a pure-Python conversion of the option dict. Useto_html()/to_option()meanwhile.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vizly-0.2.0.tar.gz.
File metadata
- Download URL: vizly-0.2.0.tar.gz
- Upload date:
- Size: 900.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b7b9a6adef4d6a82bfedd49fb636f8a6b98fe16bb2bd0920f1cda3e1593f688
|
|
| MD5 |
6f7be43935e9f08028ac6680dc80b967
|
|
| BLAKE2b-256 |
fad2a1300a88cad5aece1d55f8e9646f7071eed4e0ebf1314cef4f97761a67fd
|
Provenance
The following attestation bundles were made for vizly-0.2.0.tar.gz:
Publisher:
release.yml on kineticquant/vizly
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vizly-0.2.0.tar.gz -
Subject digest:
6b7b9a6adef4d6a82bfedd49fb636f8a6b98fe16bb2bd0920f1cda3e1593f688 - Sigstore transparency entry: 2273797363
- Sigstore integration time:
-
Permalink:
kineticquant/vizly@ce8d211eb21faac7d74392b3ba55bb5812a3c7e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/kineticquant
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ce8d211eb21faac7d74392b3ba55bb5812a3c7e6 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file vizly-0.2.0-py3-none-any.whl.
File metadata
- Download URL: vizly-0.2.0-py3-none-any.whl
- Upload date:
- Size: 902.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad382d7df432b0738c620fdfe7c6d3baab2172e8c8e0b973b0812721b9717218
|
|
| MD5 |
642a605d9a1eba9f29d076850d2c03e1
|
|
| BLAKE2b-256 |
4665733c556a76b3c00d45b3289731d8382aff7f381820f3541f9a767380a2d7
|
Provenance
The following attestation bundles were made for vizly-0.2.0-py3-none-any.whl:
Publisher:
release.yml on kineticquant/vizly
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vizly-0.2.0-py3-none-any.whl -
Subject digest:
ad382d7df432b0738c620fdfe7c6d3baab2172e8c8e0b973b0812721b9717218 - Sigstore transparency entry: 2273797422
- Sigstore integration time:
-
Permalink:
kineticquant/vizly@ce8d211eb21faac7d74392b3ba55bb5812a3c7e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/kineticquant
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ce8d211eb21faac7d74392b3ba55bb5812a3c7e6 -
Trigger Event:
workflow_dispatch
-
Statement type: