Skip to main content

vizly

Status

PyPI Python versions License: MIT CI

Built on

Python Apache%20ECharts pandas NumPy SQLAlchemy openpyxl

Frameworks supported

Streamlit FastAPI Flask Django HTMX Jupyter

Databases supported (via SQLAlchemy from_sql; install the DBAPI/dialect yourself)

SQLAlchemy included dialects (ship with SQLAlchemy; add the driver):

PostgreSQL MySQL MariaDB SQLite Oracle SQL%20Server

Also reachable when you install an external SQLAlchemy dialect (examples; not a closed list):

Snowflake BigQuery Redshift ClickHouse Databricks MongoDB

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 with to_html(), to_option(), or browser toDataURL / 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 (no vizly[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 via overlay_geojson (separate from basemap packs)
  • Drill / live update: click events for Streamlit and HTMX; set_data / window.__vizly[id].setOption for 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=False on fragments).
  • SPA / JSON: to_option() / to_json() do not auto-wire drill. Attach chart.on('click', …) yourself after echarts.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_response return the ECharts option only. They do not embed GeoJSON. HTML rendering calls echarts.registerMap for 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

vizly-1.0.1.tar.gz (928.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vizly-1.0.1-py3-none-any.whl (924.5 kB view details)

Uploaded Python 3

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

Hashes for vizly-1.0.1.tar.gz
Algorithm Hash digest
SHA256 414013ed8f68d05e59ca9feff8829743ed2b1ae78d90ea0486597c26234e9252
MD5 b82d81c48085ad8670f02adab7b852ab
BLAKE2b-256 4d67b44eade945d93bb09f0bc5698f070fe527f14ed151f7068b1d6d47bccd1b

See more details on using hashes here.

Provenance

The following attestation bundles were made for vizly-1.0.1.tar.gz:

Publisher: release.yml on kineticquant/vizly

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for vizly-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a80ec17078b72965215c71ae064b9561cb062a0f95bc658c268cc3f8dd1a0e48
MD5 f111ecd772a8eb5b5358dc6a63c2e012
BLAKE2b-256 8260f14917a1e9cb300ef2b2980e4a3ad9341c1743d810d45ad553d3d244d4b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for vizly-1.0.1-py3-none-any.whl:

Publisher: release.yml on kineticquant/vizly

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page