Skip to main content

Django and HTMX Integration with Vizly

Vizly provides first-class integrations for Django templates and HTMX partial swaps. Load ECharts once, render charts as fragments, and swap them dynamically without full page reloads.

Django template setup​

Add vizly.integrations.django to your INSTALLED_APPS and load the template tags.

# settings.py
INSTALLED_APPS = [
...
"vizly.integrations.django",
]

Basic chart in a Django template​

{% load vizly_tags %}

<head>
{% vizly_assets charts=charts %}
</head>

<body>
{% vizly_chart chart1 height="420px" %}
{% vizly_chart chart2 height="400px" %}

{# Or render from the view context #}
{{ chart3|vizly_html }}
</body>

Django view​

# views.py
import pandas as pd
import vizly as vz
from django.shortcuts import render

vz.set_theme("corporate")

def dashboard(request):
df_line = pd.DataFrame({
'date': ['2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04', '2026-01-05'],
'revenue': [42, 48, 45, 61, 58],
})
df_pie = pd.DataFrame({
'name': ['Enterprise', 'Mid-market', 'SMB', 'Startup', 'Partner'],
'value': [34, 26, 18, 12, 10],
})

chart1 = vz.line(df_line, x='date', y='revenue', title='Revenue')
chart2 = vz.pie(df_pie, names='name', values='value', title='Segment mix')

return render(request, 'dashboard.html', {
'chart1': chart1,
'chart2': chart2,
'charts': [chart1, chart2],
})

Django dashboard (one blob)​

{% load vizly_tags %}

<head>
{% vizly_assets charts=charts %}
</head>

<body>
{# Render everything in one blob, ECharts once #}
{% vizly_dashboard charts title="Analytics Dashboard" %}
</body>

HTMX integration​

Vizly ships HTMX helpers in the base package (no extra install needed). Fragments default include_assets=False so the parent page's ECharts load is reused.

HTMX view returning a chart fragment​

# views.py (using FastAPI for the HTMX example)
from vizly.integrations.htmx import htmx_chart_fragment
import vizly as vz
import pandas as pd

vz.set_theme("corporate")

@app.get("/chart/revenue")
def revenue_chart(request):
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.line(df, x='date', y='revenue', title='Revenue')
return HTMLResponse(htmx_chart_fragment(chart))

HTMX template with swap​

<!DOCTYPE html>
<html>
<head>
{% vizly_assets charts=[] %}
<script src="https://unpkg.com/htmx.org@1"></script>
</head>
<body>
<div id="chart-container">
{% vizly_chart initial_chart height="420px" %}
</div>

<button hx-get="/chart/revenue"
hx-target="#chart-container"
hx-swap="innerHTML">
Update Chart
</button>
</body>
</html>

Smart full-page vs fragment​

htmx_or_full detects HTMX requests and returns fragments for partial swaps or full HTML for direct navigation.

from vizly.integrations.htmx import htmx_or_full

@app.get("/dashboard")
def dashboard(request):
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')

return htmx_or_full(
request,
chart=chart,
template_name="dashboard.html",
# HTMX path: returns just the chart fragment
# Direct path: renders full template with chart
)

Chart click events with HTMX (v1.0.1)​

Wire ECharts click events to HTMX triggers using htmx_event_listener_js. When a user clicks a chart element, the event payload is sent as an HTMX POST to your endpoint.

from vizly.integrations.htmx import htmx_event_listener_js, htmx_chart_fragment
import vizly as vz

vz.set_theme("corporate")

@app.get("/dashboard")
def dashboard(request):
df = vz.from_records([
{'region': 'Northeast', 'sales': 86},
{'region': 'Southeast', 'sales': 64},
{'region': 'Midwest', 'sales': 71},
{'region': 'Southwest', 'sales': 58},
])
chart = vz.bar(df, x='region', y='sales', title='Sales by region', events=True)

# Generate the JS snippet for click-to-HTMX wiring
listener_js = htmx_event_listener_js(
chart_id=chart.id,
event_type="click",
hx_post="/chart/drilldown",
hx_target="#drilldown-container",
hx_swap="innerHTML",
)

return HTMLResponse(
chart.to_html() + f"<script>{listener_js}</script>"
)

@app.post("/chart/drilldown")
def drilldown(request):
payload = request.json() # { name, value, seriesName, ... }
region = payload["name"]
df = vz.from_records([
{'product': 'Widget A', 'sales': 42 + hash(region) % 40},
{'product': 'Widget B', 'sales': 38 + hash(region) % 30},
{'product': 'Widget C', 'sales': 25 + hash(region) % 20},
])
chart = vz.bar(df, x='product', y='sales', title=f'{region} - Product breakdown')
return HTMLResponse(htmx_chart_fragment(chart))

The events=True parameter on the chart enables click, hover, and other interaction events. The htmx_event_listener_js helper generates the correct JavaScript to bridge ECharts events to HTMX attribute-based swaps.

Why fragment mode matters​

Each chart embed can include the full ECharts library (~1MB). When you have multiple charts on a page:

  • Full embed mode: each chart includes ECharts independently -> N x ~1MB
  • Fragment mode: ECharts loaded once in <head> via assets_html or {% vizly_assets %}, charts include only the option JSON -> same ~1MB total
# Bad: each chart ships its own ECharts copy
chart.to_html() # full page with ECharts
chart.to_html() # another full page with ECharts

# Good: load once, render fragments
from vizly.integrations import assets_html, chart_html

assets = assets_html(charts=[c1, c2]) # in <head>
html1 = chart_html(c1, fragment=True, include_assets=False)
html2 = chart_html(c2, fragment=True, include_assets=False)

Integration summary​

FrameworkSetupChart rendering
DjangoINSTALLED_APPS + {% load vizly_tags %}{% vizly_chart %}, {% vizly_dashboard %}, {{ chart|vizly_html }}
HTMXBase package, no extrahtmx_chart_fragment(), htmx_or_full(), htmx_event_listener_js()
Streamlitfrom vizly.integrations.streamlit import st_vizly, st_dashboardst_vizly(chart), st_dashboard([c1, c2])
FastAPIfrom vizly.integrations.fastapi import html_response, json_responsehtml_response(chart), dashboard_response([c1, c2])
Flaskfrom vizly.integrations.flask import chart_html, dashboard_responsechart_html(chart), dashboard_response([c1, c2])