The Chart.js chart isn't showing

The page loads. There's a gap — or nothing at all — where the chart should be. Chart.js almost never fails loudly on the page itself, but it nearly always says something in the console. Open DevTools (F12 → Console) and match the message.

Match the console message

Console says Cause
Failed to create chart: can't acquire context from the given item The canvas wasn't found when the script ran
Uncaught ReferenceError: Chart is not defined The library never loaded
"bar" is not a registered controller. Chart.js 3+ imported without registering types
Canvas is already in use. Chart with ID '0' must be destroyed… A second chart on the same canvas
Nothing, chart area is empty or flat Zero-size container, or v2 options in v3/v4
Access to fetch at 'file:///…' … has been blocked by CORS policy Data loaded with fetch() from a local file

"Can't acquire context from the given item"

new Chart(el, config) got null. The script ran before the browser parsed the <canvas>, or the id doesn't match.

<canvas id="sales"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<script>
  // After the canvas, so getElementById finds it.
  new Chart(document.getElementById("sales"), { type: "bar", data: { … } });
</script>

If the script has to live in <head>, give it defer or wrap the code in document.addEventListener("DOMContentLoaded", …).

"Chart is not defined"

The global Chart only exists once the library's <script> has run. Check, in order:

  1. The tag is above your code. Scripts run top to bottom.
  2. The request succeeded. The Network tab shows a failed or blocked request for chart.umd.js or chart.min.js when you're offline, behind a proxy that blocks the CDN, or inside a viewer whose Content-Security-Policy refuses it — CSP versus mixed content.
  3. A local copy travelled with the page. <script src="chart.min.js"> works on your machine and 404s once only the .html file is sent — the same trap as a report that lost its CSS.

"is not a registered controller"

Chart.js 3 made the npm package tree-shakeable. With a bundler, import { Chart } from "chart.js" registers no chart types, scales or elements.

import Chart from "chart.js/auto"; // everything registered

Or register exactly what you use: Chart.register(BarController, BarElement, CategoryScale, LinearScale, Tooltip, Legend). A missing scale produces the sibling error, "category" is not a registered scale.

"Canvas is already in use"

You built a new chart on a canvas that still holds the old one — usually in a function that re-runs when a filter changes. Keep the instance:

let chart;
function render(data) {
  if (chart) chart.destroy();
  chart = new Chart(canvas, { type: "line", data });
}

Updating in place (chart.data = data; chart.update();) is cheaper and keeps the animation smooth.

The silent failures

The chart has no height. Responsive charts size from their parent. maintainAspectRatio: false tells the canvas to take the parent's height — and a div with no explicit height has none. Give the wrapper a real size and keep it for the chart alone:

<div style="position: relative; height: 320px">
  <canvas id="sales"></canvas>
</div>

It was created while hidden. A chart built inside a display: none tab, accordion or modal measures 0×0. Create it when the panel opens, or call chart.resize() then.

Options from a Chart.js 2 tutorial. scales: { yAxes: [{ … }] } and ticks: { beginAtZero: true } under yAxes are v2 syntax; v3 and v4 expect scales: { y: { beginAtZero: true } } and ignore the old shape without an error. A CDN URL without a version — npm/chart.js — serves the latest major, so a page that worked for years can change underneath you. Pin it: npm/chart.js@4.

Blank in the PDF or screenshot. The capture fired mid-animation. Set animation: false for anything printed, screenshotted or rendered headless — see HTML to PDF and HTML to image.

Data from a local JSON file. fetch("data.json") from a page opened as file:// is blocked — CORS error from origin 'null'. Inline the data in the page instead.

Or skip the script entirely

If the chart only has to be read — in a report, a pull request, an email — it doesn't need a runtime library. Paste the numbers into CSV to chart, or paste your Chart.js { labels, datasets } object straight into JSON to chart, and get a static SVG bar, line or pie chart with no script, no CDN and nothing to fail to load.

Sharing a chart that works

When the Chart.js page is right, send a link, not the file. Published to Comma, scripts run in a sandboxed iframe (allow-scripts, no allow-same-origin), so tooltips, legend toggles and animations work, and the common CDNs load inside the frame. Reviewers select the number next to the chart and comment on it in place instead of screenshotting it into chat. One caveat: a published report can't fetch() sibling files, so inline the data before you publish.

Publish a chart report →

Related