SQL is excellent at slicing and aggregating data. But a table of numbers rarely tells the full story. Wrap those query results in a chart and patterns jump out immediately — growth accelerates, one category dominates, a trend reverses.
Chart.js is one of the most popular open-source charting libraries for the web. It supports bar, line, pie, doughnut, radar, and many more chart types out of the box. Combined with sql.js (SQLite compiled to WebAssembly), you can query a database and render the result as a chart entirely in the browser — no server required.
This tutorial walks through five interactive examples, each demonstrating a different chart type on the same theme — vehicle registrations and fuel data in Australia:
- Table — preview the raw data
- Bar chart — compare EV vs diesel registrations by year
- Line chart — the same data, reframed as a trend
- Pie chart — fuel type composition
- Doughnut chart — average price by station
If you have used Python’s matplotlib or seaborn, think of Chart.js as the browser-native equivalent. Instead of plt.bar(x, y) you write new Chart(canvas, { type: 'bar', data, options }). The mental model is the same: query data, pick a chart type, map columns to axes.
The dataset
We use two tables. The first, registrations, tracks the number of new electric vehicle (EV) and diesel vehicle registrations in Australia from 2018 to 2023. The second is the same fuel table used in our other SQL tutorials — petrol station transactions with station name, fuel type, litres, and price.
Click Run on the first block to create the tables and inspect the registrations data:
EV registrations grew from around 2,200 in 2018 to over 87,000 in 2023 — a 39x increase. Diesel registrations declined steadily over the same period. A table shows the numbers, but a chart reveals the crossover point and the acceleration curve at a glance.
How the data flows from SQL to Chart.js
Each chart block below has two editors: a SQL editor on top and a Chart.js editor below it. When you click Run, the SQL query executes first and the result columns are automatically passed to the Chart.js code as variables:
headers— an array of column names, e.g.["year", "ev_count", "diesel_count"]columns[0]— all values from the first column, e.g.[2018, 2019, 2020, …]columns[1]— all values from the second column, e.g.[2216, 6718, 7248, …]columns[N]— all values from the Nth column — works for any number of columnsctx— the canvas drawing context that Chart.js renders onto
This means the Chart.js code never contains hardcoded data. If you change the SQL query — add a WHERE, swap columns, or query a different table — the same Chart.js code adapts automatically because columns[0], columns[1], etc. always reflect whatever the query returned.
Bar chart — comparing categories
A bar chart is the right choice when you want to compare discrete values side by side. Here each year is a category, and the two bars (EV vs diesel) let you instantly see which was higher and by how much:
The grouped bar chart makes the 2022 crossover obvious: EV registrations overtook diesel for the first time. In Chart.js, a grouped bar is the default when you provide multiple datasets with type: 'bar'.
Line chart — showing trends over time
A line chart emphasises the direction and rate of change. The same query, rendered as lines instead of bars, tells a story about momentum rather than discrete comparison:
Notice how the EV line curves upward exponentially while diesel slopes gently down. The line chart reveals the shape of the trend — something the bar chart hinted at but did not make as vivid. Use line charts when your x-axis is continuous (dates, years, months) and you want to emphasise trajectory.
Pie chart — showing composition
A pie chart shows how parts relate to a whole. It works best with a small number of categories where one or two slices clearly dominate. Here we use the fuel table to show the distribution of fuel types across all transactions:
With only three fuel types, the pie chart is easy to read. Unleaded dominates, followed by Diesel and Premium. If you had ten or more categories of similar size, a horizontal bar chart would be more readable — pie slices become hard to compare when they are close in size.
Doughnut chart — a cleaner ring
A doughnut chart is a pie chart with a hollow centre. The empty space can hold a label or summary statistic. It is often considered more modern and easier to read than a solid pie. Here we show the average fuel price per station:
The doughnut shows that average prices are fairly similar across stations, with Shell Fortitude Valley slightly higher. When values are close, the visual difference between slices is subtle — this is a case where a bar chart might actually be more effective. Choosing the right chart type is always about matching the question to the visual encoding.
Choosing the right chart type
Here is a quick decision guide:
- Bar chart — comparing discrete categories or side-by-side values. Best when the x-axis labels are names, not dates.
- Line chart — showing trends over a continuous axis (time, sequence). Best when you want to emphasise direction and rate of change.
- Pie chart — showing how parts make up a whole. Best with 2–5 categories where proportions differ noticeably.
- Doughnut chart — same use case as pie, but with a cleaner look and space for a centre label.
The most common mistake is using a pie chart when a bar chart would be clearer. If your audience needs to compare precise values rather than get a rough sense of proportion, bars win every time.
SQL + Chart.js vs Python (matplotlib / seaborn)
In Python, the typical workflow is df = pd.read_sql(query, conn) followed by df.plot.bar() or sns.barplot(data=df). The SQL + Chart.js approach has a different set of trade-offs:
- No server needed — sql.js and Chart.js both run in the browser. Great for tutorials, dashboards, and static sites.
- Interactive by default — Chart.js charts have hover tooltips, click events, and animation out of the box. Matplotlib charts are static images unless you add ipywidgets or Plotly.
- Less flexible for complex analysis — Python wins when you need statistical overlays, facet grids, or custom transformations that go beyond SQL.
- Styling — Chart.js uses CSS-friendly colours and responsive sizing. Matplotlib requires more manual configuration for polished output.
For exploratory data analysis, Python is hard to beat. For sharing interactive results on the web, SQL + Chart.js is a lightweight and powerful combination.
Try editing the queries above — change the ORDER BY, add a WHERE clause, or swap column names to see how the charts respond in real time.
References
- Chart.js documentation: Getting Started
- SQLite documentation: SELECT statement
- sql.js: SQLite compiled to WebAssembly
- Pandas equivalent: How to Plot with Matplotlib, Seaborn and DataFrame.plot
- Polars equivalent: How to Plot with Polars, Matplotlib and Seaborn