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

Value Widget Laravel Package

digital-creative/value-widget

A Filament widget for displaying a single value or statistic with customizable formatting, styling, and layout options. Drop it into any dashboard or page to surface key metrics, counts, or computed figures at a glance, with support for labels, icons, and descriptions.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install (pin at least ^1.0.2 — earlier 1.0.x had a reactivity bug, see Gotchas):

    composer require dcasia/value-widget:^1.0.2   # verify vendor/name against Packagist; repo is dcasia/value-widget
    php artisan vendor:publish --tag=value-widget-assets   # verify tag name against the repo
    php artisan nova:publish
    
  2. Read the source first. Small package, thin API surface — the README plus src/ is a 10-minute read. Confirm:

    • the base class name (DigitalCreative\ValueWidget\ValueWidget vs. a Nova Metric subclass),
    • the abstract method you must implement (commonly calculate() or value()),
    • the $component string used for the Vue card, so your Blade/vue assets line up.
  3. Register a widget on your dashboard (Nova 4 Dashboard class, or a dcasia/nova-dashboard layout if you use that package):

    public function cards(): array
    {
        return [
            new \App\Nova\Metrics\ActiveSubscribers,
        ];
    }
    
  4. First use case: the honest one — a single "headline number" tile (active users, revenue this month, open tickets). Build it once, verify formatting and range switching, then clone it for the rest of your KPIs.

Implementation Patterns

  • Subclass-per-metric. Keep one class per number (ActiveUsers, MonthlyRevenue) rather than parameterising a single widget with constructor args. It plays nicer with Nova's navigation and cache-key generation, and keeps calculate() testable.
  • Separate calculation from presentation. Do the aggregation in calculate() returning a plain scalar (or the package's value/delta object), and leave formatting (currency, thousands separators, suffix) to the widget's display config or the Vue component. This lets you unit-test the number without booting Nova.
  • Range handling. If the widget supports Nova-style ranges ($ranges = [30, 60, 90] or ->ranges()), implement the range argument in calculate() explicitly and cover each branch — range handling in small widgets is where silent bugs live. As of v1.0.2 the card should now re-render on range change; if you're pinned below that, verify switching ranges actually refreshes the number before trusting it.
  • Result caching. Wrap expensive aggregates:
    public function calculate(NovaRequest $request)
    {
        return cache()->remember(
            'active-users:'.$request->range,
            now()->addMinutes(5),
            fn () => User::where('last_seen', '>=', now()->subDays($request->range))->count()
        );
    }
    
    Note that the package's own cacheFor() (if present) may or may not respect the range — verify, otherwise different ranges collide on one cache key.
  • Delta/secondary values. Use the previous period as the comparison so Nova renders the up/down arrow. Keep the previous-period query symmetrical with the current one so the delta is meaningful.
  • Colour semantics. Don't rely on default green/red. For "open tickets" a rise is bad; wire a ->suffix()/->format() plus a conditional colour so the visual doesn't mislead ops users.
  • Composition. For a real dashboard, pair the value widget with a trend chart for the same query — a number alone rarely tells the story, and you already have the aggregate logic.
  • Testing. Assert calculate() returns the expected scalar with Carbon::setTestNow() and a seeded factory dataset, and assert per-range behaviour. Do not try to test the Vue side; there's not enough package surface to justify it.

Gotchas and Tips

  • Maintenance status: active again, but still thin. After a long gap since the 2023 release, v1.0.2 landed (reactivity fix, PR #4). Small win, but it's a single-commit patch on a low-star package — still pin the exact version in composer.json and be prepared to fork. The class is small; if it breaks on a Nova major, vendor it into app/Nova/Metrics and maintain it yourself.
  • Upgrade to >=1.0.2 if you switch ranges. The v1.0.2 change ("Fix reactivity issue") means earlier 1.0.x versions can fail to update the displayed value reactively — most visibly when changing the range selector or refreshing the dashboard, where you see a stale number with no PHP error. If you inherited ^1.0.0, bump and re-publish assets before debugging your query.
  • Nova-version compatibility is still unverified. The patch is a reactivity fix, not a compatibility statement. Nova 4 → 5 changed metric contracts. Install on a staging branch, hit the dashboard, and confirm the card renders before rolling out. If the Vue component uses Nova's old asset pipeline, you'll see a blank card with no PHP error — check the browser console first, not the Laravel log.
  • Asset publishing is the usual silent failure. The reactivity fix ships JS assets, so a stale published bundle looks exactly like an unfixed bug. A white or empty tile almost always means nova:publish wasn't re-run after upgrading. Add it to your deploy script rather than remembering it manually.
  • Check the cache key. If cacheFor() is implemented without incorporating $request->range (or the user), all ranges/users share one number, and the bug only appears after the first cached result expires. Reproduce with cache cleared before assuming the query is wrong — and note this masquerades as a "reactivity" problem when it isn't.
  • Timezone drift. Nova passes ranges as UTC-ish windows; if your aggregate compares created_at against local now(), daily numbers will be off by your offset near midnight. Use now()->utc() inside calculate() and verify at the boundaries.
  • N+1 on dashboards. Each widget runs its own query on page load. Three value widgets plus a chart is already four round-trips — cache aggressively, and prefer a single selectRaw aggregate over Eloquent collection counting.
  • Extension points are thin by design. Rather than fighting the package's formatting, override jsonSerialize() or swap the $component for your own Blade/vue card. That's the clean escape hatch when you need something the widget doesn't expose.
  • Debugging checklist when nothing renders (or renders stale): (1) assets re-published after the last composer update, (2) widget listed in cards(), (3) calculate() returns a non-null scalar for the current range, (4) policy/authorisation on the dashboard, (5) cache key includes the range, (6) browser console for the Vue error.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core