beyondcode/nova-custom-dashboard-card
Easily add custom dashboard cards to Laravel Nova. Render your own Blade views or Vue components as cards, giving you full control over the metrics, charts, and content shown on your Nova dashboards.
Architecture fit. The package is a single Nova Card subclass that delegates rendering to a globally registered Vue component, plus a one-line wrapper that does <component :is="card.component">. That is the entire architecture. It fits the Nova 1.x/2.x card contract precisely — a card is a PHP object with a component() name and a serialized payload — and it adds nothing else: no data layer, no caching, no layout engine, no permission model, no asset pipeline of its own. As a TPM I read that as "small blast radius, also small capability." It solves exactly one problem — escaping Nova's fixed card layouts by handing the front end a component name and an arbitrary array — and it solves it in ~2 files. Anything a stakeholder describes as a "custom dashboard" beyond that (per-persona views, nested metric panels, config-driven layouts) is our code built on top of the pattern, not functionality the package supplies.
Integration feasibility. High on paper, gated hard by version reality. On Nova 1.x/2.x with a Vue 2 toolchain, integration is a composer require, a component registration in Nova.booting, and a npm run dev. The API surface is small enough to audit in under a day, and MIT licensing permits vendoring. The real feasibility constraint is that this is a 2018 artifact: it targets Vue 2 and Nova 1.x–2.x, and on Nova 3+ API drift makes it a liability while Nova 4+ ships native multiple dashboards (Nova::dashboards()) that cover the same need with supported code. On any current-generation Nova install, feasibility is effectively "read it, rebuild it, don't install it."
Technical risk. Concentrated in ownership, not in code quality. The package is archived, last released 2018-08-22, 87 stars, zero dependents — meaning no downstream field evidence of it working in any production install we can point to, and no maintenance, no security response, no compatibility release, ever. Secondary risks are operational and land on us: ->withData() serializes straight into the HTML (Eloquent models ship more attributes than intended), inner cards built in PHP do not re-evaluate their own canSee() so authorization must be enforced where the child array is constructed, and everything in cards() executes on every dashboard load for every user. The Vue 2 → Vue 3 rebuild, if we fork, is the expensive half of any modernization — budget it separately from the PHP side.
Key questions.
Stack fit. Narrow and explicit: Laravel + Nova 1.x/2.x + Vue 2. It does not fit a modern Nova install as a dependency. The realistic fit is as an accelerator to build the composite-card pattern in-house, or as a tightly scoped dependency on a legacy admin we are not upgrading this cycle.
Migration path. Two viable routes. (1) Reference implementation: read the card class, the Vue wrapper, and the service provider, then reimplement as a first-party card under our namespace — recommended on Nova 3+, since we skip the Vue 2 toolchain problem entirely. (2) Contained adoption: vendor under our namespace, pin the Composer version, subclass CustomDashboardCard to add fluent setters rather than editing source, keep a pinned copy as a rollback artifact. Either way the exit criterion is documented before we start: if a second team requests a custom dashboard, we promote to a shared internal card library with common conventions rather than distributing forks of an archived package.
Compatibility. Three independent pins to verify — Composer/Nova version, PHP runtime (no 8.x guarantees), and the Node/webpack build (2018-era toolchain will not rebuild under current Node/Vite without work). Add smoke tests covering: card registers, asset loads, component resolves, canSee() hides it.
Sequencing. (1) Source audit — card class, Vue wrapper, service provider, publishable assets. (2) Ten-minute pipeline spike: throwaway card, verify it renders end to end; this surfaces the component-name gotcha early and cheaply. (3) First real use: full-width wrapper hosting existing Nova cards as labelled panels, children summing to 12. (4) If demand holds, config-driven per-role dashboards so adding a metric is a config line, then canSee()-gated persona variants. (5) Decision gate: promote to internal library or stop. Ship each step behind a feature flag with a documented fallback to native Nova cards.
Maintenance. We own it outright. No upstream patches, no security response, no Nova 4/5 or PHP 8.x fix will ever arrive — it rots silently. Mitigations are process, not code: pinned version, vendored rollback copy, an entry on the platform upgrade checklist, and a named owner. The class is small enough that subclassing costs nothing; direct edits to package source are a last resort and only after vendoring.
Support. No vendor SLA exists, so internal support is a runbook plus tribal knowledge. The failure modes are well characterized and worth documenting up front: a blank card almost always means ->component('X') does not resolve to a globally registered Vue component (use kebab-case everywhere); blank nested tiles mean the wrapper hand-rendered metrics instead of passing real Nova card components through, so no XHR ever fires; layouts that wrap unpredictably mean the wrapper is not full or children don't sum to 12. Triage order is browser console and Vue component tree first, PHP second.
Scaling. The scaling dimension here is query cost, not traffic. cards() runs on every dashboard view for every user, with no free caching — inline totals, counts, or churn calculations add latency to the admin home page on every load. Cache aggressively or move computation into the Vue component against a small JSON endpoint. Two additional ceilings: payloads passed via withData() are JSON in the HTML, so pass scalars or explicit ->only([...]), never raw models; and the package provides no permission model, so wrapper-level canSee() hides the panel but does not re-evaluate nested cards' authorization — scope queries by tenant and by role at the point the child array is built.
Ramp-up. Modest but front-end-weighted. A developer fluent in Laravel and Vue 2 needs roughly one to two days to ship the first composite panel; the PHP side is trivial and the JS component and build toolchain are the real work. Documentation is the source code — there is no guide beyond a minimal description — so onboarding cost is an audit, not a read. If the team lacks Vue 2 depth, or if the plan is to keep this alive across a Nova upgrade, price the fork-and-maintain path honestly: it is a small PHP class attached to an increasingly expensive front-end build.
How can I help you explore Laravel packages today?