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

Php Sparkline Laravel Package

brendt/php-sparkline

Generate lightweight SVG sparklines in PHP from arrays of numbers. brendt/php-sparkline offers a simple API for customizing colors, dimensions, and styles, making it handy for dashboards, reports, and compact data visuals.

Deep Wiki
Context7

Getting Started

  • Install with composer require brendt/php-sparkline. It's a plain-PHP package: no service provider, no config file, no vendor:publish step. It works fine inside a Laravel app but is not Laravel-specific.
  • First files to read: src/SparkLine.php and src/Day.php. The public surface is intentionally tiny — a factory on SparkLine, a Day value object, and a render() that returns an SVG string. You can absorb the whole API in a few minutes.
  • First use case: a 30-day trend beside a dashboard metric (signups, orders, errors). Build a list of Day objects, hand them to SparkLine::from(...), echo the result.
use Brendt\SparkLine\Day;
use Brendt\SparkLine\SparkLine;

$svg = SparkLine::from(
    Day::make(new DateTimeImmutable('2025-09-01'), 4),
    Day::make(new DateTimeImmutable('2025-09-02'), 9),
    Day::make(new DateTimeImmutable('2025-09-03'), 2),
    // ...
)->render();

return view('dashboard', ['sparkline' => $svg]);

Check src/Day.php for the exact Day::make() signature in your installed version — it may accept a string date rather than a DateTimeInterface — and check SparkLine for the styling setters (width/height/colour) you intend to rely on.

Implementation Patterns

Query → daily series → sparkline. The package does not query anything; you own the aggregation. Group by day in SQL, then map the result.

$rows = DB::table('users')
    ->where('created_at', '>=', now()->subDays(29)->startOfDay())
    ->selectRaw('DATE(created_at) as day, COUNT(*) as count')
    ->groupBy('day')
    ->pluck('count', 'day');

$days = collect(range(29, 0))
    ->map(fn ($i) => now()->subDays($i)->toDateString())
    ->map(fn ($date) => Day::make(
        new DateTimeImmutable($date),
        (int) ($rows[$date] ?? 0),
    ))
    ->all();

$svg = SparkLine::from(...$days)->render();

Wrap it in a small builder class. This is the single highest-leverage pattern. Put the date-range padding, query, and render() in one testable class, and inject it wherever a sparkline is needed (dashboard controller, weekly digest command, Slack notifier). It keeps the "charting" concern out of controllers and gives you one place to change colours or ranges.

Blade component wrapper. Since render() returns raw SVG, render it through a dedicated component so templates stay clean:

{{-- resources/views/components/sparkline.blade.php --}}
<div class="sparkline" role="img" aria-label="{{ $label }}">
    {!! $svg !!}
</div>

Caching. SVG output is deterministic and cheap to cache. Cache::remember("sparkline:signups:30d", now()->addMinutes(10), fn () => $builder->render(...)) removes the group-by cost on every page load. Cache the string, not the collection — no hydration cost and no serialization surprises.

Email usage. Inline SVG is stripped or mangled by several clients (notably Gmail in some contexts). For emails, prefer a data URI in an <img> (data:image/svg+xml;base64,), or render to a file and reference a hosted URL. Test in Gmail and Outlook before shipping.

Testing. Assert on coarse properties of the string (str_contains($svg, '<svg'), count of bars/paths) rather than exact path coordinates. For visual regressions, snapshot the SVG and update deliberately when you bump the package.

Other integrations. The same builder works for any count-per-day series: GitHub-style commit activity, error counts, queue throughput, API requests. If you also want a full contribution heatmap, keep this package for the sparkline and don't try to force it into that role.

Gotchas and Tips

  • Zero-fill missing days. A grouped query returns only days with rows. Feeding a sparse series into the sparkline compresses the timeline and produces a misleading shape. Always generate the continuous date range first and left-join counts onto it.
  • Timezones. created_at is typically UTC. DATE(created_at) groups by UTC day, which can shift the apparent peak for users in other zones. Decide consciously: UTC for system metrics, app/user timezone for user-facing charts.
  • Escape escaping. In Blade, {{ $svg }} will HTML-encode the markup and print it as text. Use {!! $svg !!}. For the same reason, don't run the output through any sanitizer that strips tags unless you've verified <svg> and its children survive.
  • null and bad values. LEFT JOIN yields null counts; cast to int with a default. Negative or wildly out-of-range values will distort the normalization — clamp or filter before constructing Day objects.
  • Style via the API, not string surgery. Don't regex-replace fill= or height= on the returned string. Use the package's setters/constructor options; post-processing is brittle across versions.
  • Series length. 30–90 points reads well. A full year is visually noisy and slower to render; aggregate to weekly buckets or downsample if you need long horizons.
  • Accessibility. The package won't add role="img", <title>, or aria-label for you. Wrap the SVG and provide a readable label — every sparkline in a UI should have one.
  • File caching and BOMs. If you cache rendered SVG to disk and serve it as image/svg+xml, ensure nothing (BOM, stray echo, debug output) precedes the <svg/XML declaration, or the browser will reject the image.
  • Extension strategy: compose, don't fork. The package is deliberately small. If you need annotations, tooltips, or multi-series charts, build a wrapper/builder around it rather than patching the source. SparkLine and Day are simple enough that a thin adapter in your codebase is more maintainable than a fork.
  • Pin the version. The API is minimal but not guaranteed frozen. Lock the minor in composer.json and treat upgrades as a chance to refresh snapshot tests.
  • No framework config exists. Don't look for a config file to set defaults; put those defaults in your own builder or a config array in your app.
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.
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules