vizly
Status
Built on
Frameworks supported
Databases supported (via SQLAlchemy from_sql; install the DBAPI/dialect yourself)
SQLAlchemy included dialects (ship with SQLAlchemy; add the driver):
Also reachable when you install an external SQLAlchemy dialect (examples; not a closed list):
vizly does not vendor DB drivers or run CI against every database. If SQLAlchemy can connect and return rows, from_sql can chart them. Full dialect tables: CHANGELOG / v1.0.1 release notes.
Formal v1. High-performance, low-boilerplate, fully themable Python charting over Apache ECharts.
Detailed documentation is publicly available at Rancero.
Ship production charts in a few lines of Python: DataFrame, records, columnar dict, file, or SQL in; HTML, JSON, or browser PNG out. No 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(),to_option(), or browsertoDataURL/downloadImage - Data without forced DataFrames: pass
list[dict],dict[list], loader output, or pandas (pandas remains a dependency, not a required call-site API) - SQL and files in base:
from_sql,from_csv,from_tsv,from_json,from_excel(novizly[sql]extra) - 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; GeoJSON overlays viaoverlay_geojson(separate from basemap packs) - Drill / live update: click events for Streamlit and HTMX;
set_data/window.__vizly[id].setOptionfor refresh without full re-embed - Trusted asset defaults: no Chinese CDN forced (unlike many other Python ECharts wrappers)
- Wide chart coverage: cartesian, statistical, geo, flowchart, graph, 3D, compose (
page/tab/timeline), and more - 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(...) # basemap GeoJSON you supply
vz.overlay_geojson(...) # overlay layer (not a basemap pack)
Install
Requires Python 3.9 or later.
pip install vizly
Base runtime includes pandas, numpy, sqlalchemy, and openpyxl. Install DBAPI drivers yourself for the databases you use.
Framework extras (install only what you use): vizly[streamlit], vizly[fastapi], vizly[flask], vizly[django].
Browser tests (optional): vizly[browser] + playwright install chromium.
HTMX helpers (vizly.integrations.htmx) ship in the base package. No vizly[htmx] extra.
30-second example
import vizly as vz
vz.set_theme("corporate")
# No DataFrame required:
chart = vz.line(
{"date": ["2026-01-01", "2026-01-02"], "revenue": [10, 20]},
x="date",
y="revenue",
title="Revenue",
)
chart.to_html() # self-contained local ECharts
chart.to_option() # plain dict for APIs / agents
# Browser image (after embed): window.__vizly[id].downloadImage("revenue.png")
Data in
Every chart factory accepts these shapes (and loader output):
| Source | How |
|---|---|
pandas DataFrame |
Pass directly |
list[dict] (records) |
Pass directly or vz.from_records(...) |
dict[str, sequence] (columnar) |
Pass directly or vz.from_columnar(...) |
| CSV | vz.from_csv(path_or_text) |
| TSV | vz.from_tsv(...) |
| JSON | vz.from_json(...) (records / columnar / nested data key) |
| Excel | vz.from_excel(path) |
| SQL | vz.from_sql(statement, bind=engine) or url= |
| Prometheus / CloudWatch / Elasticsearch JSON | vz.from_prometheus / from_cloudwatch / from_elasticsearch (from_elk) |
| Optional Polars / Arrow | Duck-typed when installed (not hard dependencies) |
import vizly as vz
from sqlalchemy import create_engine
table = vz.from_csv("sales.csv")
vz.bar(table, x="region", y="sales")
engine = create_engine("postgresql+psycopg://...")
sql_table = vz.from_sql("SELECT day, value FROM metrics", bind=engine)
vz.line(sql_table, x="day", y="value")
SQLAlchemy coverage: vizly charts any result set SQLAlchemy can return. vizly does not bundle every DB driver or external dialect. If SQLAlchemy can connect, vizly can chart the rows. See CHANGELOG.md for the full included-dialect and external-dialect tables (re-verified at release against SQLAlchemy docs).
Theming
vz.set_theme("corporate")
# Product: default, light, dark, corporate, minimal, contrast
# Ops-inspired: ops_grafana, ops_cloudwatch, ops_kibana
# Editor-inspired: editor_monokai, editor_tokyo_night, editor_dracula,
# editor_nord, editor_solarized_light, editor_solarized_dark, editor_one_dark
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")
vz.bar(df, x="region", y="sales", theme="editor_tokyo_night")
Ops and editor theme IDs are visual inspiration only (not affiliated with Grafana Labs, AWS, Elastic, Monokai, Tokyo Night, Dracula, Nord, Solarized, or One Dark).
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
import vizly as vz
table = vz.from_prometheus(prom_api_json) # timestamp, value, series
table = vz.from_cloudwatch(cw_datapoints) # timestamp, value [, unit|series]
table = vz.from_elasticsearch(es_search_json) # timestamp, value (alias: from_elk)
vz.set_theme("ops_grafana")
vz.line(table, x="timestamp", y="value")
These helpers do not call live APIs. They only normalize payloads you already have.
Drilldowns, events, and live update
HTML embeds emit a structured vizly:event CustomEvent and postMessage payload on click (name, value, region, breadcrumb for sunburst/treemap/tree).
from vizly.events import filter_by_click
# Host receives payload → filter sibling chart data
child = vz.bar(filter_by_click(detail_table, payload), x="category", y="sales")
# Live refresh without full page reload (host already has ECharts):
chart.set_data(new_table)
# In the browser: window.__vizly[chartId].setOption(partialOption)
- Streamlit:
st_vizly(..., events=True)returns the last click payload via a small declared component. - HTMX:
htmx_event_listener_js("/detail")POSTs clicks without reloading ECharts (include_assets=Falseon fragments). - SPA / JSON:
to_option()/to_json()do not auto-wire drill. Attachchart.on('click', …)yourself afterecharts.init.
Multi-chart pages (vz.page / dashboard_html / st_dashboard) use echarts.connect by default for linked tooltip/brush (connect=False to opt out).
Maps and geo layers
| Path | API | Role |
|---|---|---|
| Basemap packs | register_map_pack, bundled world / usa, vz.map |
Choropleth via echarts.registerMap |
| Geo overlays | overlay_geojson / GeoLayer, layers= on vz.map / vz.geo |
Points, lines, polygons on a geo coordinate system |
Join keys on choropleth: name_field= / id_field= beyond fragile name-only matching. China administrative packs remain opt-in via register_map_pack only. No Baidu Map defaults.
See examples/mapping_dashboard.py.
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, …) |
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, flowchart (alias diagram), wordcloud, geo, bar3d, line3d, scatter3d, page, tab, timeline, pictorial_bar, theme_river, liquid, surface3d
| Type | Role |
|---|---|
flowchart / diagram |
Process / dependency boxes (ECharts graph; not Mermaid) |
graph |
General networks |
tree |
Single-parent hierarchy |
sunburst / treemap |
Hierarchical part-to-whole (+ drill breadcrumbs) |
sankey |
Quantitative flow |
Extra map packs and geo layers are not chart types.
Export (PNG / image)
Images come from the browser that already rendered the chart (ECharts
getDataURL). vizly does not ship Chromium.
// After any HTML embed: chart id is on the root .vizly-chart element
const id = document.querySelector(".vizly-chart").id;
window.__vizly[id].toDataURL({ type: "png", pixelRatio: 2 });
window.__vizly[id].downloadImage("chart.png");
Optional toolbox button (no custom JS):
chart.merge_option({"toolbox": {"feature": {"saveAsImage": {"type": "png"}}}})
Live option push without re-loading ECharts:
chart.set_data(new_rows)
html_fragment = chart.live_update_script(chart_id) # inject where the chart lives
For headless batch PNG/PDF, run Playwright (or similar) on chart.to_html()
yourself. That stack is outside the vizly package (vizly[browser] is only for
gallery tests).
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, linked by default |
| JSON | APIs / SPA | to_option() / to_json() |
Multi-chart / dashboard
from vizly.integrations import assets_html, chart_html, dashboard_html
head = assets_html(charts=[c1, c2])
a = chart_html(c1, fragment=True, include_assets=False)
b = chart_html(c2, fragment=True, include_assets=False)
html = dashboard_html([c1, c2], title="Ops") # connect=True by default
Streamlit
from vizly.integrations.streamlit import st_vizly, st_dashboard
event = st_vizly(chart, height=420) # last click payload or None
st_dashboard([c1, c2], height=900) # many charts, ECharts once
FastAPI / Flask / Django
Same as before. See examples/fastapi_app.py, examples/flask_app.py, examples/django_demo/.
HTMX
from vizly.integrations.htmx import htmx_chart_fragment, htmx_event_listener_js
# Parent page: assets once + htmx_event_listener_js("/detail")
# Fragments: include_assets=False
Jupyter
Charts display via _repr_html_().
Less boilerplate than raw option builders
vz.set_theme("corporate")
vz.bar(df, x="region", y="sales", title="Sales") # ~1-2 lines
Escape hatches: chart.update(...), chart.merge_option({...}), chart.set_data(...), chart.set_option_patch(...), vz.from_option(option).
Title / legend layout
Defaults keep chrome clear of the plot: title left, legend top-right, and cartesian grid padding with containLabel. These are defaults only. Override any time:
chart.merge_option({
"title": {"left": "center", "top": 0},
"legend": {"top": "bottom", "left": "center"},
"grid": {"top": 40, "bottom": 72, "containLabel": True},
})
Or set title / legend layout keys on a custom theme (register_theme / theme=).
Examples
| Path | Purpose |
|---|---|
artifacts/docs_showcase/ |
Full v1 interactive showcase (rebuild: python scripts/build_docs_showcase.py) |
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 template tags |
examples/htmx_demo/app.py |
HTMX partial swap |
examples/mapping_dashboard.py |
Map + overlays + linked charts |
examples/ingest_demo.py |
CSV / SQL / flowchart ingest |
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
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
Docs showcase (all chart types + v1 features + themes):
python scripts/build_docs_showcase.py
# → artifacts/docs_showcase/index.html + EXAMPLES.md
Full testing runbook: TESTING.md. Also CHANGELOG.md and RELEASE.md.
Detailed documentation: Rancero vizly docs.
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. - Flowchart: process/dependency diagrams, not Mermaid syntax, BPMN, swimlanes, or sequence diagrams.
- Export: PNG/JPEG/SVG via browser
toDataURL/downloadImage. No server-side Chromium in vizly.
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-1.0.1.tar.gz.
File metadata
- Download URL: vizly-1.0.1.tar.gz
- Upload date:
- Size: 928.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
414013ed8f68d05e59ca9feff8829743ed2b1ae78d90ea0486597c26234e9252
|
|
| MD5 |
b82d81c48085ad8670f02adab7b852ab
|
|
| BLAKE2b-256 |
4d67b44eade945d93bb09f0bc5698f070fe527f14ed151f7068b1d6d47bccd1b
|
Provenance
The following attestation bundles were made for vizly-1.0.1.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-1.0.1.tar.gz -
Subject digest:
414013ed8f68d05e59ca9feff8829743ed2b1ae78d90ea0486597c26234e9252 - Sigstore transparency entry: 2281026361
- Sigstore integration time:
-
Permalink:
kineticquant/vizly@d23ada6646d8b0c5c9b923356e46a0e0d3657dfb -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/kineticquant
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d23ada6646d8b0c5c9b923356e46a0e0d3657dfb -
Trigger Event:
push
-
Statement type:
File details
Details for the file vizly-1.0.1-py3-none-any.whl.
File metadata
- Download URL: vizly-1.0.1-py3-none-any.whl
- Upload date:
- Size: 924.5 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 |
a80ec17078b72965215c71ae064b9561cb062a0f95bc658c268cc3f8dd1a0e48
|
|
| MD5 |
f111ecd772a8eb5b5358dc6a63c2e012
|
|
| BLAKE2b-256 |
8260f14917a1e9cb300ef2b2980e4a3ad9341c1743d810d45ad553d3d244d4b0
|
Provenance
The following attestation bundles were made for vizly-1.0.1-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-1.0.1-py3-none-any.whl -
Subject digest:
a80ec17078b72965215c71ae064b9561cb062a0f95bc658c268cc3f8dd1a0e48 - Sigstore transparency entry: 2281026382
- Sigstore integration time:
-
Permalink:
kineticquant/vizly@d23ada6646d8b0c5c9b923356e46a0e0d3657dfb -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/kineticquant
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d23ada6646d8b0c5c9b923356e46a0e0d3657dfb -
Trigger Event:
push
-
Statement type: