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

Chartjs Widget Laravel Package

mlsolutions/chartjs-widget

A Laravel dashboard widget that renders Chart.js charts from PHP configuration. Define datasets, labels, and options in your widget class, then display responsive, interactive charts in admin panels with minimal setup. Supports common chart types and easy customization.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and pin. composer require mlsolutions/chartjs-widget then immediately open vendor/mlsolutions/chartjs-widget/composer.json and confirm the laravel/nova and laravel/framework constraints match your install. With one star and zero dependents, this package is effectively unmaintained — pin an exact version in composer.json rather than relying on ^.
  2. Read the source before the README. The whole package is almost certainly two files: a Nova Card subclass (look in src/ for the class extending Laravel\Nova\Card) and a Vue component in resources/js/. The component() return value and the array shape passed to withMeta() are the entire API surface — confirm both.
  3. Register the card. Add it to cards() in app/Nova/NovaServiceProvider.php (default dashboard) or to a custom dashboard class in app/Nova/Dashboards/:
// app/Nova/NovaServiceProvider.php
public function cards()
{
    return [
        (new \MlSolutions\ChartJsWidget\ChartJsCard)
            ->width('2/3'),
    ];
}
  1. First use case: a 30-day trend you can't get from a Trend metric. Nova's built-in metrics aggregate one column over a period; the moment you need two series on one axis (e.g. signups vs. cancellations) or a mixed line/bar chart, this widget is the escape hatch. Start by replacing an existing dashboard card that shows a single number with a bar chart of the last 30 days — it's the smallest change that proves the data pipeline end to end.

If step 1 or 2 turns up a broken constraint (Nova 4 vs 5 asset registration is the usual culprit), abandon early: writing your own card with a Nova::script() asset and a hand-rolled Vue component is roughly a two-hour job and gives you a maintainable artifact.

Implementation Patterns

One card class per chart, data injected via withMeta(). Don't subclass the package card with hardcoded queries inline across a dozen dashboards. Create a thin abstract base in app/Nova/Cards:

abstract class BaseChart extends \MlSolutions\ChartJsWidget\ChartJsCard
{
    abstract protected function chartData(): array;
    abstract protected function chartOptions(): array;

    public function __construct()
    {
        parent::__construct();

        $this->withMeta([
            'chartData'    => $this->chartData(),
            'chartOptions' => $this->chartOptions(),
        ]);
    }
}

Then each concrete card only owns a query. Everything in withMeta() is JSON-serialized into the card's payload, so keep it to arrays/scalars — no Carbon instances, no collections (cast with ->toArray() and format dates with ->toDateString()).

Cache the aggregate, not the card. Nova re-fetches cards on every dashboard poll and every filter change. A GROUP BY DATE(created_at) over a large table will hammer the DB:

protected function chartData(): array
{
    $rows = Cache::remember('dash.signups.30d', now()->addMinutes(5), fn () =>
        Order::query()
            ->where('created_at', '>=', now()->subDays(30))
            ->selectRaw('DATE(created_at) as d, COUNT(*) as total')
            ->groupBy('d')->orderBy('d')
            ->pluck('total', 'd')
    );

    return [
        'labels'   => $rows->keys()->all(),
        'datasets' => [[
            'label'           => 'Orders',
            'data'            => $rows->values()->map(fn ($v) => (int) $v)->all(),
            'backgroundColor' => 'rgba(54, 162, 235, 0.5)',
        ]],
    ];
}

Cast the values — MySQL COUNT() and SUM() come back as strings through PDO, and Chart.js will silently plot nothing useful if handed "12" in some option paths.

Wire it to Nova's filter bar. If the card should respect dashboard date filters, call ->refreshWhenFiltersChange() and read $_GET/request filters inside your query — the widget itself won't do it for you. Same for ->refreshWhenActionRuns() if actions mutate the underlying table.

Layout deliberately. ->width('1/3'|'1/2'|'2/3'|'full') is the only sane way to compose a dashboard. Time series want full; counters and partitions want 1/3. Set an explicit height via meta if the package supports it, otherwise the Vue component's default will collide with sibling cards.

Testing. Cards are plain PHP objects — assert the serialized payload without booting a browser:

$payload = (new OrdersChart)->jsonSerialize();
$this->assertCount(30, $payload['chartData']['labels']);

That single test catches the majority of real regressions (empty datasets, string values, timezone drift) far faster than eyeballing the dashboard.

Gotchas and Tips

  • Chart instance leaks on refresh. Nova dashboards poll; each poll re-renders the card. If the package's Vue component doesn't call this.chart.destroy() in beforeDestroy, you'll accumulate Chart.js instances and see memory climb plus ghost tooltips after ~30 minutes on a wall-mounted dashboard. Patch the compiled component or override it — this is the single most likely bug you'll hit.
  • Double Chart.js registration. Nova 4+ ships Chart.js for its own Trend/Partition metrics. If this widget also registers window.Chart, you can get a version clash where the newest registration wins and breaks Nova's native metrics. Check NovaServiceProvider::boot() for what the package registers and consider removing its script if Nova's global is already present and compatible.
  • Dark mode is on you. Hardcoded #333 grid/tick colors vanish in Nova's dark theme. Read document.documentElement.classList.contains('dark') in the Vue component, or pass theme-aware colors down through chartOptions. Re-render on theme toggle if you care — most people don't, and their charts are invisible half the time.
  • Timezone skew. DATE(created_at) groups server-side in the DB session timezone; Nova's own metrics group in config('app.timezone'). A dashboard showing 23 bars next to a Trend showing 30 days is a classic. Normalize in SQL with CONVERT_TZ or group in PHP after fetching, and be consistent everywhere.
  • Nova 5 asset registration. Nova 5 moved to Vite. If the package was written for Nova 4's Nova::script(), the card will render an empty box with a console 404 for the JS bundle. Check resources/js compilation and whether you need to publish assets manually (php artisan vendor:publish). This is the main compatibility cliff between release dates.
  • Extension points are shallow. Realistically you can override component(), withMeta(), width(), and the Vue template. There's no plugin system, no option builder. Treat it as a starting point you copy into app/Nova/Cards rather than a dependency you build on — this both insulates you from abandonment and makes the code reviewable.
  • Watch the meta payload size. Putting 5,000 raw rows into withMeta() bloats the /nova-api/dashboards response and slows the initial dashboard paint. Always aggregate server-side; the client should receive pre-bucketed series, never raw records.
  • Fallback if it dies: a Nova card is ~40 lines of Vue plus a PHP class with component() and withMeta(). Register your own compiled asset in NovaServiceProvider::boot(), and you own the Chart.js version, the theme handling, and the refresh lifecycle permanently. For a package with 1 star, that trade is usually worth making on day one.
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