phpanonymous/c3js
A Laravel package that brings C3.js charting to your PHP app. Generate interactive, D3-based visualizations in Blade views using simple helpers and configurable options, streamlining chart setup without complex JavaScript. Ideal for dashboards, reports, and admin panels.
Install — composer require phpanonymous/c3js. The package sits on Packagist; verify it resolves against your Laravel version before committing (see Gotchas).
Confirm auto-discovery — open the package's composer.json. If it declares extra.laravel.providers, the C3jsServiceProvider registers itself. If not, add it manually to config/app.php. Check for a facade alias — many phpanonymous/* packages ship C3js or C3 as an alias.
Publish assets — run php artisan vendor:publish --provider="Phpanonymous\C3js\C3jsServiceProvider" (or --tag=c3js). You are looking for a config file (config/c3js.php) plus a JS/CSS bundle containing C3.js and D3. C3 will not render without D3.
Include the assets in your layout — typically via {{ c3js_assets() }}, @c3jsStyles, or <script src="{{ asset('vendor/c3js/c3.min.js') }}"></script>. Look in the package's resources/views or public directory to find the exact helper/directive name.
First use case — a single line chart on a dashboard. From a controller, build a chart through the facade and hand the resulting markup/options to the view:
// Controller
return view('dashboard', [
'chart' => C3js::line()
->categories(['Jan', 'Feb', 'Mar', 'Apr'])
->series(['Revenue' => [30, 200, 100, 400]]),
]);
{{-- View --}}
{!! $chart->render() !!}
If the package exposes a Blade component or directive instead (e.g. <x-c3js :chart="$chart" /> or @c3js($chart)), prefer that — it is usually what the docs assume.
Where to look first in the repo: src/ for the main chart class (usually a fluent builder), config/c3js.php for defaults (colors, height, responsive flag), and resources/views for the actual canvas element and JS bootstrap.
Fluent builder from the controller. The dominant pattern in this family of packages is a chainable builder that mirrors C3's generate() options object: .type('bar|line|spline|pie|donut|gauge|area'), .data(...), .axis(...), .legend(...), .tooltip(...). Keep all chart configuration in the controller or a dedicated Chart action class so views stay dumb. This also makes the chart unit-testable.
View-model / presenter. Rather than passing charts inline, wrap each dashboard widget in its own class (RevenueChart, SignupsChart) that exposes build(): C3js and render(): string. That keeps controllers thin and gives you one place to change series colors or axis labels.
Multi-chart pages. Assign each chart a unique DOM id — most wrappers auto-generate one from a hash, but confirm it (two charts sharing an id silently render only one). Set the id explicitly if the API allows: ->id('revenue-chart').
AJAX / live data. If the wrapper doesn't provide a refresh helper, render the initial chart server-side and call C3's own API on the client instead of round-tripping through the package:
chart.load({ columns: [['Revenue', 50, 120, 80, 200]] });
Store the C3 instance (c3.generate() returns it) in a window.charts map keyed by id so controllers can update charts later without a full page reload.
Blade vs. raw options. Two integration styles are common: either the package emits inline <script>c3.generate({...})</script> (convenient, hard to cache), or it emits a data attribute and you call c3.generate yourself (cacheable, CSP-friendly). Verify which one you get — it changes your CSP configuration and your ability to defer the script.
Testing. Charts should not leak into feature tests as HTML assertions. Assert on the data payload from the controller ($response->viewData('chart')->toArray() or equivalent) rather than the rendered JS blob. If the package has no toArray(), snapshot the config object instead.
d3.scale.category10 is not a function. Either downgrade to D3 v3, load D3 locally with noConflict, or load charts on pages that don't pull modern D3."phpanonymous/c3js": "1.*" or a hash range) and pin the CDN/bundle version too.src/ before trusting the README. Budget an hour for source reading before you build anything nontrivial.illuminate/support constraint is current. If Composer refuses to install on Laravel 10/11, you may need --ignore-platform-reqs locally, or fork and widen the constraint in composer.json. Prefer forking over ignoring platform reqs in production.extra.laravel.aliases. If C3js:: throws "Class not found," add it to config/app.php. Debug by running php artisan about or dumping app()->getLoadedProviders() to confirm the provider is live.vendor:publish, changes to the config file require php artisan config:clear. In production, run config:cache after publishing, otherwise defaults reverted on deploy will surprise you.' in a category label breaks the script. Sanitize or json_encode labels yourself if you control the payload.->id( and assign explicit ids everywhere.c3/d3 reference, (2) confirm the <div id> exists in DOM at call time (defer or DOMContentLoaded), (3) log the generated options object — most "blank chart" reports are an empty columns array from a broken query.->toArray() or similar), mutate it, and call c3.generate() yourself. That leaves you free to replace the wrapper later without rewriting chart code.How can I help you explore Laravel packages today?