How to build interactive charts in python using htmx and echarts
This article will show you how to build interactive charts in python using htmx and echarts. You will learn how to use include other libraries in your htmx workflow using htmx extensions.
Themba Mahlangu

In this post, we will show you how to include javascript libraries like echarts into your htmx workflow using htmx extensions.
Prerequisites
Basic knowledge of pyecharts & htmx - If you haven't already, please check out this post about how to set up pyecharts for your Django/Python application.
The code for this tutorial can be found here
Concepts Covered
We want to make our filters to the chart so we can filter on dates.
-
htmx extensions
- we will create an htmx extension to handle parsing data from the backend and updating the chart.
-
triggering client side
- actions from the backend
-
hyperscript
- a small example of how to use the hyperscript library with htmx
Chart View
We will create a view to handle htmx requests. The view will use parameters provided in the request to return data for the correct period and chart type.
def chart_view_hx(request):
"""Returns chart options for echarts"""
period = request.GET.get("period", "week")
chart_id = request.GET.get("chart_id")
chart_type = request.GET.get("chart_type")
days_in_period = {
"week": 7,
"month": 30,
}
filter_by = days_in_period.get(period,"week")
# simulate fetching this from your database
chart_title = CHART_TYPES.get(chart_type, "page_views")
chart_data = fake_chart_data(filter_by, chart_title)
# render the chart and update options to include id
chart = line_chart(chart_data)
chart.options["id"] = chart_id
chart._prepare_render()
data = chart.json_contents
response = HttpResponse(content=chart.json_contents)
# optional
# if using the django_htmx library you can attach any clientside
# events here . For example
# trigger_client_event(response, 'filterchanged', params={})
return responseNotes
period = request.GET(..)
- fetch the period, chart_id, chart_type from the request
chart_data = fake_chart_data()
- this simulates fetching data from an api or your database.
chart = line_chart(chart_data)
- call the method that build the chart. This method will return an instance of a Line chart with all the options we need to update the chart
chart.options["id"]
- here we add the id of the chart we want to update and then call the prepare_render method to ensure that the data is converted to json.
trigger_client_event
-
If you need to piggyback additional events to be triggered on the client-side, you can attach them here.
If you are using Django, the django_htmx library provides the trigger_client_event method for this
You can also do this for yourself by attaching the HX-Trigger to the response. See the htmx docs for more details on this.
Update the urls
- django_project/urls.py
urlpatterns = [
...
path('charts', chart_view_hx, name='charts-hx')
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)Notes
path('charts', ..)
- add the view to urls.py.
static(settings.STATIC_URL..)
- You will notice we have also added the Django
statichelper function here so we can staticfiles in debug mode.
Add htmx extension
Htmx provides an extension mechanism for defining and using extensions within htmx-based applications.
htmx.defineExtension('echarts', {
transformResponse: function (text, xhr, elt) {
// parse json data
var data = JSON.parse(text);
// fetch echart element
var option = data;
var chartContainer = document.getElementById(data.id);
var chart = echarts.getInstanceByDom(chartContainer);
// clean up options and update chart
delete data.id;
chart.setOption(option);
}
});Notes
htmx.defineExtension('echarts', {}
- define an extension called echarts. This will be referenced on the element by setting the
hx-extattribute i.e.
<div hx-ext='echarts'>
var data
= JSON(parse(text)) - parse the chart data from the backend
var chartContainer
= document.getElementById(data.id); - fetch the chartContainer element from the DOM using the id provided in our data
delete data.id
- we clean up our options object to remove the id. We only needed the Id so we could identify the chart to update.
chart.setOption(option)
- finally, use the
setOption()method from the echarts library to update the data on the chart.
Update the templates
Next, we need to update our chart template to include the filter buttons.
We will create a new filter_button component for the filters.
<button
hx-get="{% url 'charts-hx' %}?chart_id={{ c.chart_id }}&chart_type=page_views&period={{period}}"
hx-swap="none"
id="id_last_week_filter_btn"
class="bg-white rounded shadow p-2 text-sm flex items-center font-semibold mr-2 cursor-pointer
{% if selected %}text-indigo-600 {% else %}text-gray-600 {% endif %}">{{label}}
</button>Notes
hx-get="{% url 'charts-hx' %}
... - include the url and required parameters
hx-swap ='none'
- set this to none, the response from the backend will not be swapped out.
{{chart_type}},{{chart_id}},{{period}}
- the component expects these variables to be available in the context
{% if selected .. %}
- here we are adding a bit of styling to highlight the selected button.
The main template will remain largely unchanged from the previous tutorial. We have modified it to include the filter_buttons. To keep things simple, we have removed the additional charts.
<head>
...
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<script src="{% static 'js/ext/hx-echarts.js'%}"></script>
...
</head>
<div class='flex' hx-ext="echarts">
{% include 'components/chart_filter.html' with period='week' c=charts.0 label='Last Week' selected=True %}
{% include 'components/chart_filter.html' with period='month' c=charts.0 label='Last Month' %}
</div> Notes
<head >..</head>
- the head section has been modified to include the htmx library, the echarts extension script.
{% include 'components/chart_filter.html'
- include the components in the main template and define the variables for the chart, label, the period to be used in the filter as well as the
UI Improvements
We can improve it by letting our users know which tab is active. This can be achieved using vanilla javascript or alpinejs. In this example, we will use hyperscript, a companion library for htmx to achieve this.
<head>
...
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.5"></script>
<script src="{% static 'js/ext/hx-echarts.js'%}"></script>
...
</head>
<div class='flex' hx-ext="echarts"
_="on htmx:afterOnLoad take .text-indigo-600 for event.target"
>
{% include 'components/chart_filter.html' with period='week' c=charts.0 label='Last Week' selected=True %}
{% include 'components/chart_filter.html' with period='month' c=charts.0 label='Last Month' %}
</div>Notes
<head></head>
- we have updated the head section to include hyperscript.
_="on htmx:afterOnLoad take
..." after the response is received, the text-indigo-600 class will be moved to the clicked button.
Conclusion
In this post, we learned how to use htmx extensions with the echarts library. The htmx library provides a convenient mechanism to organize your codebase and integrate other libraries into your htmx workflow.