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

Charts Laravel Package

grafite/charts

Grafite Charts is a Laravel package for building interactive charts with minimal setup. It wraps Chart.js-style visualizations into simple PHP/Blade helpers, letting you render line, bar, pie, and other charts directly from your application data.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin Laravel wrapper around Chart.js in the lineage of ConsoleTVs/Charts: you define a chart as a class, set labels and datasets in a boot method, and the package emits an HTML container plus the matching JS initialiser.

composer require grafite/charts

Publish whatever config ships with it (php artisan vendor:publish --provider="Grafite\Charts\ChartsServiceProvider", or --tag=charts — check vendor/grafite/charts/src for the exact tag) so you can pin the Chart.js version/CDN the package injects. If your app already bundles Chart.js through Vite, you can usually skip the package's CDN helper and load the library yourself.

First place to look is the package source, not the README: src/Chart.php is the whole contract. Everything you get is labels(), dataset(), container(), script(), and a boot() hook to override. That is roughly 200 lines — read it once and you know the package.

First use case, the one you'll actually ship on day one: a dashboard tile.

namespace App\Charts;

use Grafite\Charts\Chart;

class RevenueChart extends Chart
{
    public $title = 'Revenue';
    public $type = 'line';
    public $height = 300;
    public $width = 600;

    public function boot()
    {
        $this->labels(['Jan', 'Feb', 'Mar', 'Apr']);

        $this->dataset('2025', 'line', [1200, 1900, 1500, 2400]);
        $this->dataset('2024', 'line', [900, 1100, 1000, 1400]);
    }
}

Controller and view:

public function index(RevenueChart $chart)
{
    return view('dashboard', compact('chart'));
}
{!! $chart->container() !!}

@push('scripts')
    {!! $chart->script() !!}
@endpush

Verify the dataset() argument order (label, type, data vs label, data, type) against src/Chart.php before you write ten chart classes with the wrong one — it is the single most common way people waste an hour with this package.

Implementation Patterns

One class per chart, data injected not queried. The idiomatic usage keeps boot() free of Eloquent calls and lets the chart be constructed with data. Either resolve query results in the controller and pass them via a constructor, or bind a small query object:

class RevenueChart extends Chart
{
    public function __construct(public Collection $rows) {}

    public function boot()
    {
        $this->labels($this->rows->pluck('month')->all());
        $this->dataset('Revenue', 'line', $this->rows->pluck('total')->all());
    }
}

This matters because chart classes are instantiated per request, and a chart that runs three aggregate queries inside boot() will be the slowest thing on your dashboard the moment someone adds date filters.

Mixed chart types are the main reason to use datasets at all — pass 'bar' for one series and 'line' for another and set the chart $type to 'bar'; Chart.js renders the mix. Stacked bars work the same way via options.

Layout partial. Render the container/script pair once in a Blade component so you're not repeating @push('scripts') in every view:

{{-- resources/views/components/chart.blade.php --}}
<div class="chart-wrapper" style="height: {{ $chart->height }}px">
    {!! $chart->container() !!}
</div>

@once
    @push('scripts')
        {!! $chart->script() !!}
    @endpush
@endonce

AJAX / Livewire / polling. The container and script are just strings, so you can return the script from a JSON endpoint and eval-inject it client-side, or return ['html' => $chart->container(), 'script' => $chart->script()] and swap the DOM node before running the init. The package has no state — it is a string builder — so this works without fighting it.

Date-filtered dashboards. Make the chart class accept a CarbonPeriod or a Filter DTO and build labels from the period rather than from the data, so empty months still render as gaps instead of silently shifting the x-axis.

Caching. Wrap the resolved dataset array in Cache::remember("chart.revenue.{$range}", 300, ...) in the controller/service and pass the array in. Chart classes themselves are not cacheable; the data behind them is.

Gotchas and Tips

License is NOASSERTION. GitHub could not detect a standard license file, which means you have no clear grant to use, modify, or redistribute this. For an internal dashboard it is probably fine in practice, but get it in front of whoever signs off on dependencies before it lands in a product. This is the single biggest reason to consider ConsoleTVs/Charts or plain Chart.js with a hand-rolled Blade partial instead.

4 stars, 0 dependents. There is no community to answer questions, no Stack Overflow corpus, and bug reports go into a void. Budget for reading the source and being willing to fork or patch in vendor/ (better: repositories with a VCS path or a local fork) rather than waiting on upstream. Pin the version in composer.json and commit the lock file.

Chart.js version mismatch is the #1 runtime failure. If $chart->script() emits new Chart(ctx, ...) and your bundle loads Chart.js 2, or the package's CDN helper emits a v2-style config against a v4 build, you get a blank canvas and a console error, not a PHP exception. Check the emitted <script> tag and match the version your options array was written for — v2→v3/v4 changed scales, tooltips, and legend config structurally.

Container ID collisions. If the same chart appears twice on a page (a widget and a modal), container() will reuse the generated ID and only one will initialise. Confirm whether the package derives the ID from the class name — if so, subclass or add a uuid to the class name for the second instance.

Escaping and XSS. Labels frequently come from user-controlled data (category names, tenant names). The script output JSON-encodes the dataset, but check for json_encode flags — if labels can contain </script> and the output lands in an inline <script> block, you have an injection vector. Prefer passing labels you control or run them through a sanitiser.

Numbers arrive as strings. pluck() off a query builder gives strings from MySQL; Chart.js will happily plot "1200" sometimes and silently misbehave with scale stacking other times. Cast to float in the dataset builder.

Height/width vs. responsive. Setting $height/$width on the class plus Chart.js maintainAspectRatio defaults produces charts that don't resize with the container. Wrap the canvas in a fixed-height div and set responsive: true, maintainAspectRatio: false in the options.

Debugging flow. Blank chart → view-source the rendered page and confirm both the <canvas> and the init script are present → check the network tab for Chart.js actually loading → check console for Chart is not defined (CDN blocked/missing) vs. a config-shape error (version mismatch). That order resolves 90% of issues.

Testing. Assert $response->assertSee($chart->container(), false) to catch a broken chart class without a browser, and unit-test boot() output by exposing labels/datasets — cheap coverage for the data-shaping logic, which is where the actual bugs live.

Extension points are thin. Everything you'd want to customise (tooltips, axis formatting, plugins) goes through the options array rather than a first-class API. If you need anything beyond labels plus datasets, plan on the raw Chart.js options and treat the package as a convenience wrapper, not an abstraction worth defending.

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.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections