robertboes/blade-selfhst-icons
Blade components for rendering selfh.st icons in your Laravel views. Drop self-hosted app and service icons into Blade templates with simple, reusable tags, keeping your dashboards and admin panels visually consistent without manual SVG handling.
Install the package and confirm the icon set ships in the vendor tree (it is a Blade component package, so there is usually no publish step required for the SVGs themselves):
composer require robertboes/blade-selfhst-icons
Where to look first:
vendor/robertboes/blade-selfhst-icons/ — read README.md, then the service provider to see how components are registered and where the SVG directory lives.config/blade-selfhst-icons.php (if published/available) — controls the component prefix, default icon path, fallback behaviour and any caching options. Publish it only if you need to override defaults.First use case — drop a self-hosted service logo into a dashboard tile or bookmark list:
<x-selfhst-icon name="home-assistant" class="h-6 w-6" />
The component name and default prefix are defined by the package; check the README for the exact tag before wiring it into many views, because renaming the tag later is a wide-blast-radius change.
Treat the icon set as a catalogue, not a design asset. The typical workflow is: a Service model / config array holds a slug (e.g. proxmox, grafana, jellyfin), and the view resolves it to an icon:
<x-selfhst-icon :name="$service->slug" class="h-8 w-8 shrink-0" />
Wrap it once. Create your own resources/views/components/service-icon.blade.php that sets sizing, aria-hidden, and a fallback, then forbid direct usage of the vendor tag in app code. This gives you one place to react to upstream renames (see Gotchas) and one place to enforce accessibility:
@props(['name', 'size' => 'h-6 w-6'])
<x-selfhst-icon
:name="$name"
{{ $attributes->merge(['class' => "$size shrink-0"]) }}
aria-hidden="true"
focusable="false"
/>
Pair with a text label. Brand icons are visually dense; in nav lists and app grids, always render the icon next to the service name and mark the icon decorative. Only give the SVG a <title>/aria-label when it stands alone (e.g. a compact mobile tile).
Use it in Livewire/Filament/mail contexts deliberately. Because these are inline SVGs, they work inside Livewire components without extra plumbing, but avoid looping hundreds of them into a single response — see the performance tip below. For mail, inline SVG support is poor in several clients; prefer the package's PNG/other output if it offers one, or fall back to a plain text label.
Keep a test that asserts icon existence. A cheap Pest/PHPUnit test over your service catalogue is the single highest-value integration:
it('has an icon for every service', function () {
foreach (Service::pluck('slug') as $slug) {
$this->blade("<x-selfhst-icon name=\"{$slug}\" />")->assertSee('<svg', false);
}
});
This converts an upstream icon removal from a silent production gap into a failing build.
composer update can remove a name your app depends on. Pin the version and review the diff of the SVG directory when bumping, or rely on the existence test above.home-assistant rather than homeassistant, and check whether variants (light/dark, wordmark, mono) are separate names or a separate attribute. Normalise slugs in one place (a model accessor or a small map) rather than in views.text-* colours often do nothing. Many brand icons are multi-colour with hardcoded fills, so text-blue-500/fill-current will not recolour them. For monochrome/tinting needs, confirm per-icon which ones are currentColor-based; if you need uniform tinting, prefer the mono variant or a CSS filter.icons:cache-style artisan command) or a config flag to pre-compile, enable it; otherwise cache the rendered tiles at the fragment/view level."h-{$size}"), Tailwind's scanner will miss them — use a fixed map of allowed size classes instead.composer update.grep the icon name in the vendor SVG directory, then dump the component's resolved attributes with {{ $attributes }} inside your wrapper. Nine times out of ten it is a name mismatch or a stray fill inherited from a parent element.How can I help you explore Laravel packages today?