plotly/plotly.js
Plotly.js is an open-source JavaScript charting library for interactive, publication-quality graphs. It supports 40+ chart types, including statistical, 3D, scientific, and SVG/WebGL visualizations, with declarative APIs and MIT licensing.
Package identity mismatch. plotly/plotly.js is not a Laravel/PHP package. It is a browser-side JavaScript charting library (MIT, ~18.4k stars) with no Composer distribution, no service provider, no PHP API, and no server-side renderer. Any Laravel integration is therefore an asset + JSON contract integration, not a package install. This reframes the evaluation: we are adopting a frontend dependency and owning the PHP-side glue ourselves.
Architecture fit. Good, with conditions. Plotly consumes a declarative figure spec (data + layout + config) that maps cleanly onto a PHP DTO/serializer. That gives a natural seam: Laravel builds and validates the spec server-side; the browser renders it. Fits Blade + Vite, Livewire/Alpine partial updates, and Inertia/Vue/React equally. It does not fit server-rendered contexts — PDF invoices, queued emails, OG images, or anything requiring a PNG without a browser.
Integration feasibility. Low friction to first chart, moderate friction to production-grade:
<script> (fastest spike, worst for CSP/SRI/vendor drift).PlotlyChart value object, a Blade component, and a small JS bootstrapper (~150–300 LOC total).Technical risk.
| Risk | Severity | Note |
|---|---|---|
| Bundle weight | High | Full build is multi-MB; partial bundles (scatter, bar, gl2d) are essential or TTI on mobile suffers |
CSP / unsafe-eval |
High | Plotly has historically relied on dynamic function construction in parts of its attribute pipeline; verify against our CSP before committing |
| No native server-side export | Medium–High | Static images require headless Chrome (Browsershot/Puppeteer) or the Python kaleido sidecar — a second runtime we would have to operate |
| Accessibility | Medium | Canvas/SVG hybrid; screen-reader and keyboard support is limited vs. native HTML charts |
| Upgrade cadence | Medium | Major versions carry breaking spec changes; no Laravel-aware semver signal |
| Data quality flag | Low | Reported "last release 2026-10-05" is a future/unreliable date — treat registry metadata as untrusted and pin explicitly |
Key questions for the team.
scattergl/WebGL and server-side downsampling?Stack fit. Laravel 10/11/12 + Vite is the happy path: npm i plotly.js-dist-min (or a custom partial bundle via plotly.js/lib/core + register), imported once in resources/js/app.js, exposed on window.Plotly or via an Alpine/Livewire directive. Blade renders a <div data-chart='@json($spec)'>; a single delegated initializer handles mount, resize, and teardown.
Recommended shape.
App\Support\Charts\Figure — immutable PHP value object with data(), layout(), config(); enforces a schema and strips user-supplied callbacks.App\Support\Charts\PlotlyRenderer — serializes to JSON, applies org-wide theme defaults (palette, fonts, margins, displayModeBar), and sets a config.responsive + staticPlot policy.<x-plotly-chart :figure="$figure" /> Blade component — emits escaped JSON in a data attribute (never inline {!! !!}), plus a stable id.resources/js/plotly.js — registry keyed by element, ResizeObserver, Livewire hook for wire:model re-renders (Plotly.react, not newPlot).ChartSpec API resource for SPA/Inertia consumers.Migration path. If we are coming from Chart.js/ApexCharts, do not port chart-by-chart. Introduce the Figure abstraction first, ship 2–3 representative charts behind it (time series, categorical bar, mixed axis), and keep the old library mounted for the remainder. This caps blast radius and gives a clean deletion point. If greenfield, start with Plotly directly but keep the same abstraction — it is our hedge against swapping renderers later.
Compatibility. Browser floor: evergreen Chrome/Edge/Firefox/Safari; Plotly's own support matrix governs, and it moves faster than Laravel's. Pin the version in package.json and record it in the Figure output (config.plotlyVersion) so cached specs can be invalidated on upgrade. Verify Livewire 3 morphing does not double-mount; the wire:navigate lifecycle needs explicit teardown.
Sequencing.
Figure, renderer, Blade component, Alpine/Livewire hook, one chart in production behind a flag.Maintenance. We own the glue entirely — roughly 300 lines of PHP/JS plus tests. The dependency itself is actively maintained and MIT, so no licensing exposure, but there is no Laravel-community maintainer to escalate to. Realistic ongoing cost: one dependency bump per quarter, each needing a visual-regression pass and a check on spec-level breaking changes. Majors (v2→v3 class events) are the expensive ones; budget a day each.
Support. Expect recurring, non-obvious tickets: blank chart from a malformed spec (silent failure — Plotly logs to console, not the UI), charts not resizing inside tabs/modals (fix: ResizeObserver + Plotly.Plots.resize on visibility change), CSP console errors after a security-header change, and mobile memory crashes on large traces. Add a try/catch around newPlot with a rendered fallback and a client-side error beacon, or these arrive as unexplained "page is broken" reports.
Scaling. Rendering cost is entirely client-side, so server load is near-zero — attractive for a PHP fleet. The bottleneck moves to the browser: SVG traces degrade past a few thousand points, so high-cardinality series need scattergl (WebGL), server-side aggregation, or downsampling. Cap traces per figure and points per trace in the Figure validator rather than discovering it in production. Cached specs (Redis, keyed on query params + data version) keep PHP response times flat. If we add server-side PNG export, that becomes a new scaling problem — headless Chrome is memory-hungry and must be isolated on its own queue with concurrency limits.
Failure modes.
<script src>.wire:navigate / Turbo-style morphing → duplicate canvases and memory leaks; explicit teardown required.<table> alternative now, not at audit time.Ramp-up. A PHP developer ships a basic chart in under a day — the figure spec is JSON and the docs are extensive. Proficiency with partial bundles, WebGL traces, subplot geometry, and custom hover/click event wiring takes two to three weeks and requires genuine JS comfort. Plan for one designated owner with frontend chops rather than distributing ownership across the team; the failure mode for shared ownership here is an unpinned, unupgraded, CDN-loaded library that nobody wants to touch. Given the low opportunity score (31.2) and the absence of a PHP-native surface, treat this as adopt-and-own infrastructure, not a dependency we can lean on a vendor for.
How can I help you explore Laravel packages today?