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

Axis Laravel Package

epessine/axis

Axis is a modern, ultra-simple and flexible Laravel library for drawing charts entirely in PHP—no JavaScript boilerplate. It's built for Livewire and supports Chart.js, ApexCharts and Highcharts out of the box, with no configuration hell. Just install and draw.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and first render (under an hour).

composer require epessine/axis

Publish the config only if you need to change defaults or register a custom library driver — the package is designed to work without it. Then, in a Blade view, build and echo a chart:

{{ \Axis::chartjs()->bar()->labels(['Jan','Feb','Mar'])->series([12, 19, 7])->title('Revenue') }}

That single expression is the whole pitch: the fluent builder emits a native Chart.js config object, and the Blade render wires it into a canvas. No Vite entrypoint, no new Chart(ctx, ...).

Where to look first, in order:

  1. The docs site (epessine.github.io/axis/docs) — specifically the fluent API page and the per-library pages. It is the only spec you have.
  2. src/ for the driver classes (ChartJs, ApexCharts, Highcharts). The package is small; when docs and behaviour disagree, the source wins.
  3. The Blade renderer component — this is where the JS library is expected to be loaded, and therefore where most "blank canvas" problems originate.
  4. config/axis.php after publishing — confirms asset loading strategy and any CDN/version pins.

First use case to target: a bar or line chart on an internal admin dashboard, driven by a single Eloquent aggregate. Something like:

$orders = Order::query()
    ->selectRaw('DATE(created_at) as day, SUM(total) as revenue')
    ->groupBy('day')->orderBy('day')->get();

Chart::chartjs()->line()
    ->labels($orders->pluck('day'))
    ->series($orders->pluck('revenue'));

Resist starting with the Livewire attribute or image rendering. Prove the plain Blade path first; it isolates asset-loading problems from everything else.

Implementation Patterns

Centralize construction behind one seam. Do not scatter Chart::chartjs()->... calls across controllers and views. Create App\Support\Charts\ChartFactory (or a Chart base class per report) and route every chart through it:

final class RevenueChart
{
    public static function make(Collection $rows): mixed
    {
        return Chart::chartjs()->bar()
            ->labels($rows->pluck('label'))
            ->series($rows->pluck('value'));
    }
}

This gives you one place to enforce a house palette, one place to add empty-state handling, and — critically — makes the dependency a one-file swap if you fork or replace Axis later. Keep each chart's underlying configuration expressible as a plain array so exit is copy-paste.

Data shaping happens in SQL, not PHP. Charts are a presentation concern; the series arrays should be pluck() of an already-aggregated query. Never hydrate a thousand models to produce twelve bars. ->pluck('day') and ->pluck('revenue') on an aggregate result is the canonical shape.

Multiple datasets. For comparisons (this month vs. last), chain series calls or pass an array of datasets — check the driver page, since the signature differs subtly between Chart.js and ApexCharts and was refactored in v0.2.1. Verify with the docs for your pinned version.

Livewire pattern. Axis is built for Livewire, and the #[Axis] attribute is the reason to care. Without it, a Livewire re-render tears down and rebuilds the canvas, which is slow and loses animation state. With it, the chart instance survives the round trip:

class SalesDashboard extends Component
{
    #[Axis]
    public function revenue(): mixed
    {
        return Chart::chartjs()->line()
            ->labels($this->range->days())
            ->series($this->range->revenue());
    }
}

Because the chart is a method/property on the component, a date-range wire:model change re-emits the config and the existing instance updates in place. This is the single highest-value thing the package does — hand-rolling it is fiddly and most teams get it wrong.

Escape hatches, used deliberately. Two exist:

  • Raw config: Chart::chartjs([...]) accepts the native config array. Use this for the ~20% of options the fluent API doesn't model, rather than fighting the builder.
  • Scriptable callbacks: Script::from('...') injects a JS function into a config property that expects one — tick formatters, tooltip callbacks, datalabels. This is where you'll spend your indirection tax: you are writing JavaScript inside PHP strings, with no syntax highlighting and no type checking.

Testing. Assert on the generated config array in a unit test rather than eyeballing the browser — the builder is pure PHP, so $chart->toArray() or the public config accessor is a stable CI assertion. Reserve browser checks for the one or two charts where the visual shape actually matters. If you find no assertion surface at all, that's a signal to keep charts confined to low-stakes surfaces.

Image rendering (v0.6.0+). The feature exists for PDFs and emails, but verify its runtime requirements against your deploy image before committing to a roadmap item. Server-side PNG generation frequently drags in Node or Chromium, which is a material container-size and cold-start change.

Library choice is separable from Axis. Chart.js is MIT and the safe default. ApexCharts is fine. Highcharts carries commercial licensing that Axis does not alter — get explicit approval per surface if you go there, and keep the decision in the factory, not scattered.

Gotchas and Tips

Pin an exact version. Pre-1.0 with breaking changes in minor releases: series() params refactored in 0.2.1, IsFluent renamed to AsAxisChart in 0.6.0. Use "epessine/axis": "0.8.0", not ^0.8. Configure Renovate/Dependabot to route Axis bumps to a human, never auto-merge.

Don't pin below 0.7.1. A bug in options() on the Highcharts and ApexCharts drivers was fixed there. If you're on an older lockfile and options silently vanish, that's why.

Blank canvas with no server-side error is the #1 failure mode. The PHP side always succeeds — it produced a valid config array. The failure is client-side: the JS library didn't load, loaded twice, or is a different major version than the config targets. Debugging order: (1) dump() the config array and confirm it's well-formed, (2) check the browser console for Chart is not defined / constructor errors, (3) verify the library version actually delivered to the page against what the driver expects. Pin the JS library version and record the PHP↔JS pairing in the repo.

Asset delivery is the unresolved question — answer it early. Determine whether the package pulls Chart.js from a CDN, expects you to publish assets, or integrates with Vite. Each has different caching, CSP, and offline implications. If you have a strict Content-Security-Policy, a CDN default will be blocked and every chart will fail identically; budget for publishing assets locally.

Script::from() is an injection surface. It writes a JavaScript function into the rendered config. Any chart whose config is influenced by user or tenant input must never interpolate that input into Script::from(). Enforce this in the factory — a single wrapper method that rejects dynamic arguments is cheaper than a code review habit.

Cast your numerics. Values plucked straight from selectRaw may arrive as strings depending on the driver. Chart libraries will often render them, but axis scaling, tooltips, and sorting misbehave. Explicitly ->map(fn ($v) => (float) $v) on the series.

Payload size and re-render cost. Every chart is an independent library instance with its own canvas; five charts on a page is five initializations. Worse, in Livewire the full dataset is serialized into the diff on every round trip — a date-range picker that re-emits five charts with 500 points each is a visible lag. Downsample server-side, cap series length, and cache aggregates rather than recomputing per keystroke.

Empty states, accessibility, and dark mode are yours. A chart with a zero-length series renders as an empty box with axes; no server-side signal fires. Handle it in the factory before the builder runs. Likewise, screen-reader table fallbacks, aria labelling, and palette conformance to your design system are not evidenced as addressed by the package — budget them where the charts are customer-facing.

Livewire upgrades are your highest-risk breakage. #[Axis] necessarily relies on Livewire internals to preserve instance state. A Livewire major is the most likely single-event break, and it lands on a framework upgrade you cannot defer. Budget a half-day per major upgrade cycle to validate chart rendering across every surface, and keep a documented vendor/fork procedure — there is no one upstream to escalate to.

Read the source as the spec. 27 stars, a single maintainer, self-described beta, no CVE process, no Stack Overflow corpus. The docs are decent but will have gaps. When a fluent method doesn't do what you expect, open the driver class — it's a few hundred lines and greppable. That's the real cost model: not license fees, but being your own vendor for upgrades and defects. For internal dashboards and a PHP-heavy team, that trade is usually favourable. On a revenue-critical customer surface with no fork plan, it isn't.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast