laurentmuller/highcharts-bundle
Laravel bundle for Highcharts: build interactive JavaScript charts from PHP with a fluent, chainable API. Define chart options, series, axes, and styling in Laravel controllers or views, then render responsive Highcharts visualizations without writing raw JavaScript.
This is a Symfony bundle, so the honest first step for a Laravel developer is deciding whether you're porting it or replacing it. If you want to evaluate it anyway:
composer require laurentmuller/highcharts-bundle — expect it to pull in ghunti/highcharts-php (the actual chart-building library) and Symfony components. In a Laravel app you'll get the library but not the auto-wiring, Twig extension, or config tree.DependencyInjection/ and Resources/config/services.* — this is the only place that shows what services exist (highcharts factory, chart renderer).highcharts factory class: it exposes newChart(), newStockChart(), newGaugeChart(), etc., returning a Ghunti\HighchartsPHP\Highchart instance.ghunti/highcharts-php README for the option/series API — that's where the real usage lives.highcharts + highcharts-vue/manual init, or CDN). The bundle does not ship assets; it only emits configuration and an init script.// app/Http/Controllers/DashboardController.php
public function index(Highchart $chart) // bind the factory in a provider
{
$chart->setTitle(['text' => 'Revenue by month']);
$chart->xAxis->categories = ['Jan', 'Feb', 'Mar'];
$chart->addSeries(['name' => '2024', 'data' => [12, 19, 8]]);
return view('dashboard', ['chartScript' => $chart->render()]);
}
{{-- resources/views/dashboard.blade.php --}}
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="revenue"></div>
{!! $chartScript !!}
Wrap the factory in a service provider. Laravel won't load the Symfony bundle, so register the pieces you need manually:
// app/Providers/HighchartsServiceProvider.php
public function register(): void
{
$this->app->singleton(\Ghunti\HighchartsPHP\Highchart::class, fn () => new \Ghunti\HighchartsPHP\Highchart());
}
If you want to keep the bundle's highcharts factory behavior, copy its ~20 lines of factory into a HighchartsFactory class rather than fighting the container.
Prefer the JSON/API pattern over server-rendered scripts. The most Laravel-idiomatic use is a dedicated endpoint returning option arrays, with the chart built client-side. This keeps {!! !!} escaping out of your views, survives Livewire/Inertia, and lets you cache chart data independently:
public function revenue(): JsonResponse
{
return response()->json([
'title' => ['text' => 'Revenue by month'],
'xAxis' => ['categories' => ['Jan', 'Feb', 'Mar']],
'series' => [['name' => '2024', 'type' => 'column', 'data' => [12, 19, 8]]],
]);
}
fetch('/api/reports/revenue')
.then(r => r.json())
.then(opts => Highcharts.chart('revenue', opts));
Blade component for the container + init. Encapsulate the <div id> and script tag so pages don't diverge:
{{-- resources/views/components/chart.blade.php --}}
@props(['id', 'options'])
<div id="{{ $id }}"></div>
@push('scripts')
<script>Highcharts.chart(@json($id), {{ Illuminate\Support\Js::from($options) }});</script>
@endpush
Theme/config presets. The bundle's Symfony config YAML (global Chart defaults, global credits, lang) has no Laravel equivalent. Translate those keys into a config/highcharts.php file and merge with array_replace_recursive at render time — this gives you a per-environment default and is the one bundle concept worth keeping.
Testing. Assert on the option array / JSON, never on the rendered HTML string. The generated script tag is brittle and changes with Highcharts minor versions.
bundle auto-discovery, no Twig extension, no highcharts_render_chart() Blade function. Every one of those is a manual port. Budget a half-day for the first chart and treat the bundle as a reference implementation, not a dependency.composer.lock and have an exit plan (the option arrays are portable — dropping the dependency later is mostly copy-paste).composer.json require before you try installing on 8.3; you may need --ignore-platform-reqs, which is a smell.render() returns a <script> block. In Blade you must use {!! !!}. Prefer Illuminate\Support\Js::from() for any user-influenced values, and never let request data reach setTitle() unescaped.tooltip.formatter, plotOptions.series.point.events.click, and xAxis.labels.formatter need raw JS functions. The upstream library has quirks here (__raw__-style markers or string functions); if you use @json/Js::from you'll get "function () {...}" as a string and the chart renders without the callback. Use Js::from() only for values, and hand-place function blocks in Blade.highcharts/modules/exporting vs exporting.src), Highcharts.chart() is fine but Highcharts.stockChart/Gantt need explicit module imports, and credits display rules changed. If you're on v11+, assume the bundle's output needs adjustments.dd(json_encode($chart->getOptions(), JSON_PRETTY_PRINT)) (or the equivalent accessor) tells you instantly whether a blank chart is a server-side options bug or a client-side load-order bug. Blank chart + no console error almost always means Highcharts JS loaded after your init script.newStockChart, newGaugeChart) are thin and easy to replicate; the series/options passthrough is the actually valuable part. Skip the Symfony config tree, Twig extension, and asset assumptions entirely.ConsoleTVs/Charts (though stale), Livewire chart components, or just the raw option-array + Highcharts.chart() pattern above are lower-risk and give you full control over Highcharts version and callback handling.How can I help you explore Laravel packages today?