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