Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ux Chartjs Laravel Package

symfony/ux-chartjs

Symfony UX Chart.js integrates the Chart.js library into Symfony apps, letting you render interactive charts directly from PHP with Twig components and Stimulus controllers, no custom JavaScript required.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package (PHP 8.1+, Symfony 6.4/7.x — v3.6.0 is the current line, replacing the v3.5.x series):

    composer require symfony/ux-chartjs
    

    Pin explicitly if you need reproducible builds: composer require symfony/ux-chartjs:^3.6.

  2. Enable the bundle (Flex does this automatically; add it manually only for non-Flex apps):

    // config/bundles.php
    return [
        // ...
        Symfony\UX\ChartJsBundle\ChartJsBundle::class => ['all' => true],
    ];
    
  3. Install Chart.js — the package ships the Stimulus controller but not the library itself:

    composer require chartjs/chart.js   # Flex/AssetMapper route
    # or
    npm install chart.js                # Webpack Encore / Vite route
    
  4. Render a chart (both syntaxes work in UX 3.x — the component() function and the Twig component tag):

    <twig:ChartjsChart
        type="line"
        :data="{
            labels: ['Jan', 'Feb', 'Mar'],
            datasets: [{ label: 'Sales', data: [10, 20, 30] }]
        }"
        :options="{ responsive: true }"
    />
    
  5. Rebuild assets after the upgrade — v3.6.0 bumped the package's own tooling dependencies (#4086), so expect a lockfile refresh on the first npm install / importmap:install after upgrading:

    php bin/console importmap:install && php bin/console asset-map:compile
    # or
    npm install && npm run build
    

Where to Look First

  • vendor/symfony/ux-chartjs/templates/components/ChartjsChart.html.twig — the canonical attribute list (type, data, options, class).
  • vendor/symfony/ux-chartjs/assets/src/controller.js — the Stimulus controller; this is what you subclass or listen to for live updates.
  • vendor/symfony/ux-chartjs/README.md — note that install snippets now use an X.Y.Z placeholder (#3907); substitute the real version rather than copy-pasting literally.

First Use Case: Static Dashboard Chart

Start with a plain controller + Twig render; reach for LiveComponent only once you need reactivity.

// src/Controller/DashboardController.php
#[Route('/dashboard', name: 'dashboard')]
public function dashboard(SalesRepository $sales): Response
{
    return $this->render('dashboard.html.twig', [
        'chartData' => [
            'labels' => $sales->monthlyLabels(),
            'datasets' => [[
                'label' => 'Revenue',
                'data'  => $sales->monthlyTotals(),
                'backgroundColor' => 'rgba(59, 130, 246, .5)',
            ]],
        ],
    ]);
}
{# templates/dashboard.html.twig #}
<twig:ChartjsChart type="bar" :data="chartData" :options="{ responsive: true }" />

Implementation Patterns

Core Workflows

  1. Data Binding

    • Static data: pass plain PHP arrays through Twig — Chart.js only ever sees JSON, so there is no DTO/entity mapping layer to learn.
    • Dynamic data: wrap the chart in a LiveComponent and re-render the whole component; the Stimulus controller tears down and rebuilds the Chart.js instance on viewValueChanged.
      #[AsLiveComponent]
      final class SalesChart extends AbstractController
      {
          #[LiveProp(writable: true)]
          public string $range = '30d';
      
          public function getData(): array { /* ... */ }
      }
      
      <twig:SalesChart :range="range" />
      
    • API-polled data: fetch JSON in the Stimulus controller and call chart.data = …; chart.update() rather than round-tripping through PHP.
  2. Chart Customization

    • Options map 1:1 to the Chart.js options object — anything valid in Chart.js is valid here, since it is serialized straight through.
      :options="{
          plugins: { legend: { position: 'bottom' }, title: { display: true, text: 'Monthly Sales' } },
          scales: { y: { beginAtZero: true } }
      }"
      
    • Plugins must be registered before the chart mounts, in your app entrypoint:
      // assets/app.js
      import { Chart, registerables } from 'chart.js';
      import annotationPlugin from 'chartjs-plugin-annotation';
      Chart.register(...registerables, annotationPlugin);
      
  3. Live Updates via the Stimulus Controller

    • Access the instance from a wrapper controller through the chartjs:connect event, or subclass the package controller:
      // assets/controllers/polling_chart_controller.js
      import ChartjsController from '@symfony/ux-chartjs/controller';
      import { Controller } from '@hotwired/stimulus';
      
      export default class extends Controller {
          connect() {
              this.element.addEventListener('chartjs:connect', (event) => {
                  this.chart = event.detail.chart;
                  this.timer = setInterval(() => this.refresh(), 30_000);
              });
          }
      
          async refresh() {
              const data = await (await fetch(this.element.dataset.url)).json();
              this.chart.data = data;
              this.chart.update();
          }
      
          disconnect() { clearInterval(this.timer); }
      }
      
  4. Reusable Components

    • For a handful of chart shapes, a thin Twig wrapper keeps call sites readable:
      {# templates/components/Chart/Line.html.twig #}
      <twig:ChartjsChart type="line" :data="data" :options="{ elements: { line: { tension: 0.4 } } }" />
      
    • For logic (aggregation, permissions, formatting), a LiveComponent is the better seam than a Twig include.

Integration Tips

  • AssetMapper: no build step needed beyond importmap:require chart.js; the package's own JS is imported via @symfony/ux-chartjs/controller.
  • Encore / Vite: add chart.js to package.json and, for Vite, pre-bundle it:
    // vite.config.js
    optimizeDeps: { include: ['chart.js', 'chartjs-plugin-annotation'] }
    
  • Turbo / partial navigation: charts re-mount on turbo:load automatically because the Stimulus controller reconnects — do not cache chart canvases in Turbo frames without data-turbo-permanent care.
  • Progressive enhancement: render the <canvas> with aria-label and a fallback <table>; charts are invisible to screen readers otherwise.

Gotchas and Tips

Common Pitfalls

  1. symfony/asset-mapper 8.2+ conflict — fixed in v3.6.0 (#3919)

    • Older releases (<= v3.5.0) conflict with symfony/asset-mapper 8.2 and later, typically surfacing as missing/duplicate importmap entries or a Cannot find asset error at asset-map:compile time.
    • Fix: upgrade to symfony/ux-chartjs ^3.6. If you must stay on an older ux-chartjs, pin symfony/asset-mapper to <8.2.
    • After upgrading, clear the asset cache so stale manifest entries don't linger:
      php bin/console cache:clear && php bin/console asset-map:compile
      
  2. Stale JS after the tooling bump (#4086)

    • v3.6.0 refreshed the package's dev/tooling dependencies. Vendored asset output is unchanged in behaviour, but a stale importmap.php/node_modules can produce duplicate Chart.js registrations ("line" is not a registered controller or double-drawn tooltips). Delete node_modules/vendor artifacts and reinstall once.
  3. Twig component syntax confusion

    • Both {{ component('chartjs_chart', {...}) }} and <twig:ChartjsChart … /> are valid; {% use %} is Twig template inheritance and has nothing to do with components. The X.Y.Z placeholder in the README (#3907) is the other common copy-paste trap — substitute real versions.
  4. Chart.js version conflicts

    • UX 3.x targets Chart.js v4. [email protected] and other v3-era plugins will silently no-op or throw on tree-shaken registrations. Verify plugin major versions match.
    • Tree-shaken builds need explicit registration (Chart.register(LineController, …)), otherwise the chart mounts as an empty canvas with no console error.
  5. LiveComponent re-render churn

    • Every LiveProp change destroys and recreates the Chart.js instance, losing zoom/pan/legend-toggle state. Use data-symfony--live--… model updates sparingly, or keep the instance and mutate chart.data from Stimulus instead.
  6. Serialization mismatches

    • PHP arrays with non-sequential keys serialize to JSON objects, and Chart.js expects arrays for labels/data. Always array_values() your labels and dataset series.
    • Dates and DateTimeInterface objects do not serialize — format to Y-m-d strings in PHP, or use Chart.js' time scale with parsing: false.

Debugging Tips

  • Empty canvas, no error: 99% of the time Chart.js is not registered or the controller never connected. Check application.debug = true and look for chartjs:connect firing on the element.
  • Inspect component state: {{ dump(component('chartjs_chart', { type: 'line', data: chartData })) }} renders the resolved props without mounting.
  • Inspect the live instance: Chart.getChart(canvasElement) in the console returns the mounted instance, or undefined if nothing mounted — a fast way to separate a JS problem from a data problem.
  • Asset pipeline: php bin/console debug:asset-map | grep chart confirms the controller is mapped; php bin/console importmap:audit catches version conflicts after an upgrade.
  • Payload shape: log the rendered data-* attribute (data-symfony--ux-chartjs--chart-view-value) — it is exactly the JSON Chart.js receives, which short-circuits most "why is my chart blank" investigations.

Extension Points

  1. Custom Stimulus controller — extend @symfony/ux-chartjs/controller to add refresh(), export-to-PNG, or drill-down click handlers; dispatch your own events from it so other controllers can react.
  2. Chart.js plugins — annotations, zoom, datalabels all register the same way; keep registration in assets/app.js so it happens once, not per component.
  3. LiveComponent wrapper — the cleanest place for aggregation queries, per-user filtering, and cache headers; expose only the final data/options arrays to Twig.
  4. Mercure / SSE — subscribe in the Stimulus controller from a hub URL passed as a component prop, then mutate chart.data and call chart.update('none') to avoid animation stutter on high-frequency updates.

Configuration Quirks

  • Asset paths: UX 3.x assets live under assets/src/, not Resources/assets/; upgrade guides referencing the old path will silently break importmap entries.
  • asset_mapper: when using AssetMapper you do not set framework.asset_mapper: true manually — the bundle's recipe does it, and the 8.2 conflict above is the thing to watch.
  • Version placeholders: README commands use X.Y.Z since v3.6.0; substituting a placeholder literally produces a Could not find package … X.Y.Z failure that reads like a network problem.
  • CSP: Chart.js writes into a <canvas> and inline styles; if you run a strict Content-Security-Policy, add style-src 'unsafe-inline' or Chart.js' resize observer will fail silently.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource