cachethq/badger
Badger is CachetHQ's badge generator, producing SVG status badges for your Cachet status page components so you can embed live uptime and status indicators in READMEs, dashboards, or docs.
Badger is Cachet's SVG badge generator for Laravel — a self-hosted shields.io for things like build status, coverage, version numbers, and download counts that you can drop into a README or dashboard.
Minimum setup:
composer require cachethq/badger
v5 requires PHP 8.2+ and targets Laravel 11/12/13. If you're on Laravel 8–10, pin the 4.x branch (composer require cachethq/badger:^4.0) — do not attempt to force v5 onto an older skeleton.
Then:
composer show cachethq/badger and confirm the service provider is auto-discovered.php artisan vendor:publish --tag=badger-config or equivalent — check vendor/cachethq/badger/config for the exact tag) and read config/badger.php first; that's where the default style, color, and route prefix live.php artisan route:list | grep -i badge to see the registered badge endpoint(s).First use case: render a static badge in a Blade view or Markdown file.
{!! Badger::badge('build', 'passing', 'green') !!}
Or hit the HTTP endpoint and paste the URL into README.md — that's the day-to-day use for most teams.
Facade / inline rendering. Use the Badger facade when you already have the data in PHP and want to embed the SVG directly:
use CachetHQ\Badger\Facades\Badger;
return view('dashboard', [
'coverage' => Badger::badge('coverage', '87%', 'yellowgreen'),
]);
Cast to string (or ->toSvg()) when you need the raw markup — useful for caching the output in Redis/file storage rather than regenerating per request.
Dynamic badge endpoints. The highest-value pattern is a small controller behind the package's route that computes a value live:
Route::get('/badge/tests', function () {
$count = Cache::remember('badge.tests', now()->addMinutes(10), fn () => Tests::count());
return response(Badger::badge('tests', (string) $count, 'blue'))
->header('Content-Type', 'image/svg+xml');
});
Wrap the DB query in Cache::remember — badges get hit by GitHub's camo proxy and CI scrapers far more often than you'd expect.
Style selection. v5 adds the for-the-badge style, modeled on shields.io, alongside the existing flat/plastic variants. Pass the style wherever the badge is constructed, or expose it as a query parameter on your own endpoint so consumers can pick:
// e.g. /badge/tests?style=for-the-badge
Badger::badge('tests', '342', 'blue', style: 'for-the-badge');
Confirm the exact parameter name/signature against the package source — the positional arguments have historically been subject, status, color.
README / docs integration. Point the URL at your app's domain and commit the URL, not the SVG, so the badge stays live. If the repo is public but the app is private, keep badge endpoints unauthenticated (and rate-limited) or render badges at build time into a static file.
Testing. v5's suite is on Pest 3/4 + Testbench 10/11, so mirror that for your own app tests. Assert on the response content type and on presence of the subject/status text rather than byte-matching the SVG:
$this->get('/badge/tests')
->assertOk()
->assertHeader('Content-Type', 'image/svg+xml')
->assertSee('tests', false);
v3.0.0...v5.0.0 and re-read the facade signature before upgrading. Treat the constructor/argument order as potentially changed.null positionally into badge methods may now fail loudly. Audit any Badger::badge(..., null) calls.4.x. Don't let a composer update silently try to resolve v5 — constrain the version explicitly if you're on an older skeleton, and be aware that future security fixes may only land on the 5.x line.for-the-badge is visually wide. The uppercase, bolder, letter-spaced style takes noticeably more horizontal space than flat. In README tables or sidebars it can wrap or overflow — prefer it for hero badges, not dense badge rows.- becomes --, _ becomes __, and spaces are _. If you're generating badge URLs from user-controlled strings, this is a correctness bug and an injection surface.{!! !!}-rendered markup. Never pass raw request input into the subject/status without stripping <, >, &, and quotes — otherwise you've built a stored-XSS vector on a .svg response that browsers will happily render as a document.Cache-Control: public, max-age=300 plus a short server-side cache is the right default; add ETag if you want GitHub's camo to revalidate instead of refetching.route:list first, then the config's route prefix, then whether a catch-all route in your app is shadowing the badge path. If the SVG renders but looks wrong, the style string is the usual culprit — an unrecognized style typically falls back to a default rather than erroring.vendor/cachethq/badger/resources/views before writing your own SVG builder.v* tags. If you mirror or vendor this package, pin a tag rather than a branch — the changelog is regenerated on release, so diffs between tags are the source of truth.How can I help you explore Laravel packages today?