Skip to main content

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 with to_html() or to_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: 39 registered chart types including cartesian, statistical, geo, graph, 3D, and compose
  • Native integrations: Streamlit, FastAPI, Flask, Django, HTMX, and Jupyter

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.


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

Chart inventory

vz.list_chart_types()
vz.list_unavailable_chart_types() # e.g. chord (upstream-unavailable)
CategoryChart types
Cartesianline, bar, area, scatter, effect_scatter
Statisticalboxplot, heatmap, parallel, radar, polar
Financialcandlestick, kline, waterfall
Pie-likepie, donut, funnel, treemap
Geographicmap, geo
Relationalgraph, sankey, tree, sunburst
3Dbar3d, line3d, scatter3d, surface3d
Specializedgauge, liquid, wordcloud, pictorial_bar, theme_river
Composegrid, page, tab, timeline
Mixedmix / combo

Upstream unavailable

chord: ECharts 5.x no longer ships a first-class chord series; use sankey or graph instead.


Theming

vz.set_theme("corporate")
# builtins: default, light, dark, corporate, minimal, contrast,
# ops_grafana, ops_cloudwatch, ops_kibana
vz.set_theme({"palette": ["#0B1F33", "#2F6FED"]}) # deep-merge override
vz.register_theme("acme", {"background": "#FFFFFF", "palette": ["#111111"]})
vz.load_theme("path/to/theme.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.

Theme API

FunctionPurpose
vz.set_theme(name_or_dict)Session theme (dict deep-merges)
vz.get_theme() / resolve_theme()Read or resolve theme
vz.register_theme(name, dict)Register a custom preset
vz.load_theme(path, activate=, register=)Load theme from JSON file
vz.export_theme(name, path)Export a registered theme
vz.list_themes()List available theme names

Per-chart theme= kwarg overrides without mutating session theme.


Core API

Every chart factory lives on the vizly namespace (import vizly as vz).

import vizly as vz

# Sugar constructors
chart = vz.line(df, x='date', y='revenue', title='Revenue')
chart = vz.bar(df, x='region', y='sales', title='Sales')
chart = vz.scatter(df, x='height', y='weight', title='Height vs weight')

# Universal constructor by type name
chart = vz.chart('area', df, x='date', y='revenue', title='Area')

# Wrap a raw ECharts option
chart = vz.from_option({...})

# Export
chart.to_html() # self-contained HTML with local ECharts
chart.to_option() # plain dict for APIs / SPAs
chart.to_json() # JSON string

# Escape hatches
chart.update(...)
chart.merge_option({...})

Export paths

ExportRole
chart.to_html()Self-contained HTML with local ECharts (vendored JS inlined)
chart.to_option()Plain dict for APIs, SPAs, or agents
chart.to_json()JSON string of the option
chart.render("file.html")Write self-contained HTML to a file

Base lifecycle: data standardization -> _build() -> theme apply -> user merge -> export.


Integrations

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="Dashboard")
# same idea: vz.page(charts=[c1, c2]).to_html()

Streamlit

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

FastAPI

from vizly.integrations.fastapi import html_response, json_response, dashboard_response

Flask

from vizly.integrations.flask import assets_html, chart_html, dashboard_response

Django templates

INSTALLED_APPS = [..., "vizly.integrations.django"]

# Template:
{% load vizly_tags %}
{% vizly_assets charts=charts %}
{% vizly_chart chart height="420px" %}
{{ chart|vizly_html }}

HTMX

from vizly.integrations.htmx import htmx_chart_fragment, htmx_or_full

Jupyter

Charts display via _repr_html_() on chart objects.


Ops metric helpers

Normalize observability JSON you already have - no live API calls.

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")

Maps and geography

ItemDetail
Default atlasWorld (vz.map(df))
Bundled regionalusa (vz.map(df, map="usa"))
Opt-in packsregister_map_pack(name, geojson_path) for custom GeoJSON you supply
Introspectionlist_bundled_maps(), list_opt_in_maps()

SPA / JSON note: to_option() / json_response return the ECharts option only. HTML rendering calls echarts.registerMap for you.


Trust and assets

RuleDetail
Default modeassets.mode = "local" (vendored JS inlined or linked from package)
Optional CDNAllowlist only: cdn.jsdelivr.net, unpkg.com
Banned by defaultChina-primary CDN hosts (bootcdn, npmmirror, assets.pyecharts.org, ...)
Plugin loadingGL, wordcloud, liquidfill load only when the chart needs them

Pinned vendored assets

FileVersion
echarts.min.js5.5.1
echarts-gl.min.js2.0.9
echarts-wordcloud.min.js2.1.0
echarts-liquidfill.min.js3.1.0
maps/world.jsonecharts 4.9.0 map pack
maps/usa.jsonapache/echarts-examples

All examples use the corporate theme. Each chart below shows the complete Python code.

Chart Gallery

See all chart types rendered live with theme switching in the Vizly Chart Gallery -- no Python needed.

area

df = pd.DataFrame({
'date': ['2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04', '2026-01-05'],
'revenue': [42, 48, 45, 61, 58],
})
chart = vz.area(df, x='date', y='revenue', title='Revenue trend', theme='corporate')

bar

df = pd.DataFrame({
'region': ['Northeast', 'Southeast', 'Midwest', 'Southwest', 'West', 'Canada'],
'sales': [86, 64, 71, 58, 93, 47],
})
chart = vz.bar(df, x='region', y='sales', title='Sales by region', theme='corporate')

scatter

df = pd.DataFrame({
'height': [1.86, 1.726, 1.893, 1.829, 1.588, 1.94, 1.854],
'weight': [58.2, 74.8, 62.9, 84.2, 73.0, 92.0, 85.6],
})
chart = vz.scatter(df, x='height', y='weight', title='Height vs weight', theme='corporate')

pie

df = pd.DataFrame({
'name': ['Enterprise', 'Mid-market', 'SMB', 'Startup', 'Partner'],
'value': [34, 26, 18, 12, 10],
})
chart = vz.pie(df, names='name', values='value', title='Segment mix', theme='corporate')

map

df = pd.DataFrame({
'name': ['United States', 'Canada', 'Brazil', 'Germany', 'United Kingdom'],
'value': [92, 54, 61, 48, 57],
})
chart = vz.map(df, title='Global demand', theme='corporate')

candlestick

df = pd.DataFrame({
'date': ['2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04', '2026-01-05'],
'open': [100, 104, 101, 108, 112],
'close': [104, 101, 108, 112, 109],
'low': [98, 99, 100, 106, 107],
'high': [106, 105, 110, 114, 114],
})
chart = vz.candlestick(df, x='date', title='Price action', theme='corporate')

combo

df = pd.DataFrame({
'date': ['2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04', '2026-01-05'],
'revenue': [42, 48, 45, 61, 58],
'cost': [18, 20, 19, 24, 22],
})
chart = vz.combo(df, x='date', bar='cost', line='revenue', title='Revenue vs cost', theme='corporate')

page (dashboard)

vz.set_theme('corporate')
chart = vz.page(charts=[
vz.line(df_line, x='date', y=['revenue', 'cost'], title='Revenue vs cost'),
vz.pie(df_pie, names='name', values='value', title='Segment mix'),
vz.map(df_map, title='Global demand'),
], title="Dashboard")

What's New in v0.2.1

Release date: 2026-07-28

v0.2.1 is a patch release focused on packaging metadata and a safer publish path. Product API and chart behavior match v0.2.0.

Added

  • Release workflow verify gate (ruff + Level 1/2 pytest) before build/publish
  • GitHub Release on v* tags (CHANGELOG body + dist/ artifacts)
  • Packaging contract tests: __version__ sync with pyproject.toml; runtime requires pandas and numpy

Changed

  • PyPI / package summary: high-performance, low-boilerplate, fully-themable charting over Apache ECharts
  • Declare runtime dependencies pandas and numpy in package metadata
  • Release workflow fails when ENABLE_PYPI_PUBLISH is not set

v0.2.0 highlights (new since v0.1.0)

  1. Multi-chart dashboards: load ECharts once per page across Streamlit, Django, Flask, FastAPI, and HTMX
  2. Smarter asset loading: assets_html(charts=...) auto-include GL and plugin scripts
  3. Public map introspection: list_bundled_maps() and list_opt_in_maps()
  4. Three-level test suite: contract tests, sample/golden option snapshots, Playwright browser smoke
  5. GitHub Actions CI and release: lint, pytest with coverage, build on every PR
  6. Locale and asset trust refinements: theme locale passed to echarts.init

Known limitations

  • PNG/PDF export: vizly[export] is deferred; use to_html() / to_option() meanwhile
  • SPA JSON + maps: client must call echarts.registerMap for map/geo charts
  • Streamlit: each st_vizly single-chart call uses a separate iframe; prefer st_dashboard for many charts

License

MIT