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

Bootstrap Icons Laravel Package

twbs/bootstrap-icons

Official open source SVG icon library for Bootstrap, offering over 2,000 free, MIT-licensed icons. Install via npm (bootstrap-icons) or Composer, then use as embedded HTML, img tags, an SVG sprite, or CSS. Also available in Figma.

View on GitHub
Deep Wiki
Context7

Getting Started

Two install paths, and you should pick one deliberately:

# Front-end (recommended when you already run Vite)
npm i bootstrap-icons

# Back-end only (no Node in the build, or you just need the raw SVGs)
composer require twbs/bootstrap-icons

Neither package ships a Laravel service provider, so there is no vendor:publish. After installing, immediately confirm what you actually got:

ls vendor/twbs/bootstrap-icons   # icons/  font/  bootstrap-icons.svg  package.json
ls node_modules/bootstrap-icons  # same layout

The three artefacts that matter day-to-day:

Artefact Path Use
Individual SVGs (~2,000 files) icons/*.svg Inline Blade component, per-icon control, currentColor
Icon font + CSS font/bootstrap-icons.css, font/bootstrap-icons.min.css, font/fonts/*.woff2 Fastest to author, <i class="bi bi-alarm"></i>
Sprite bootstrap-icons.svg One HTTP request for many icons

First use case — a sidebar nav in your existing layout. Wire the font once in resources/sass/app.scss:

@import "bootstrap-icons/font/bootstrap-icons.css";

include it via @vite(['resources/sass/app.scss']), then in resources/views/layouts/app.blade.php:

<nav class="nav flex-column gap-1">
    @foreach (config('nav.items') as $item)
        <a class="nav-link d-flex align-items-center gap-2"
           href="{{ route($item['route']) }}">
            <i class="bi bi-{{ $item['icon'] }}" aria-hidden="true"></i>
            {{ $item['label'] }}
        </a>
    @endforeach
</nav>

That is the five-minute version. Everything below is about making it survive production.

Implementation Patterns

1. Inline SVG Blade component (the workhorse)

The font is convenient but loads ~100 KB of woff2 and is invisible until it arrives (font-display: block was added in v1.8.2). For anything above the fold, inline the SVG so it paints immediately and inherits currentColor:

// app/View/Components/Icon.php
namespace App\View\Components;

use Illuminate\Support\Facades\Cache;
use Illuminate\View\Component;

class Icon extends Component
{
    public string $name;

    public function __construct(string $name)
    {
        // Never let a DB/user-supplied value reach the filesystem raw.
        $this->name = preg_replace('/[^a-z0-9\-]/', '', strtolower($name));
    }

    public function inner(): string
    {
        $file = base_path("vendor/twbs/bootstrap-icons/icons/{$this->name}.svg");

        if (! $this->name || ! is_file($file)) {
            return '';
        }

        return Cache::rememberForever(
            'bi:'.$this->name.':'.filemtime($file),
            fn () => preg_replace(
                '/^.*?<svg[^>]*>|<\/svg>.*$/s', '', file_get_contents($file)
            )
        );
    }

    public function render()
    {
        return view('components.icon');
    }
}
{{-- resources/views/components/icon.blade.php --}}
@if ($inner = $this->inner())
    <svg {{ $attributes->merge([
            'width' => '1em', 'height' => '1em',
            'fill' => 'currentColor', 'aria-hidden' => 'true',
            'focusable' => 'false',
         ]) }}>{!! $inner !!}</svg>
@endif

Usage is then uniform everywhere: <x-icon name="trash" class="text-danger" />. Because you strip the outer <svg> and re-emit your own attributes, width="1em" scales with font-size, and class="fs-4" on a parent works as expected.

2. Let blade-ui-kit/blade-icons own the lookup

If you already use @svg() in views, register Bootstrap Icons as a set instead of writing the component:

// config/blade-icons.php
'sets' => [
    'bi' => [
        'path' => 'vendor/twbs/bootstrap-icons/icons',
        'prefix' => 'bi',
    ],
],

Then @svg('bi-alarm', 'w-4 h-4 text-secondary') in any Blade file, plus <x-bi-alarm />.

3. Sprite for dense screens

Admin tables with an action icon per row generate hundreds of inlined SVGs. Ship one sprite instead:

// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [laravel({ input: ['resources/js/app.js'], refresh: true })],
    publicDir: 'public',
});

Copy node_modules/bootstrap-icons/bootstrap-icons.svg into public/build/ (or a public/vendor/icons directory committed/vendored at build time), then:

<svg class="bi" width="16" height="16" aria-hidden="true">
    <use href="{{ asset('vendor/icons/bootstrap-icons.svg') }}#pencil-square"></use>
</svg>

Same-origin only, and <use> on external files does not inherit currentColor in every browser — verify in Safari before committing to it.

4. Validate icon names against the shipped manifest

font/bootstrap-icons.json is a name → codepoint map. Use it to keep icon names out of free-text DB columns:

// app/Rules/IconName.php
public function passes($attribute, $value): bool
{
    return array_key_exists(
        $value,
        json_decode(file_get_contents(base_path('vendor/twbs/bootstrap-icons/font/bootstrap-icons.json')), true)
    );
}

The same JSON powers a Livewire/select2 icon picker in an admin panel: build the options once, cache them, and you get a searchable visual picker for ~2,000 icons for free.

5. Non-Vite / legacy deployments

If your build pipeline is Mix or you deploy --no-dev, vendor/ is wiped on every deploy and is not web-served. Add a tiny copy step rather than referencing vendor/ from asset():

// app/Console/Commands/PublishIcons.php
$src = base_path('vendor/twbs/bootstrap-icons/font');
File::ensureDirectoryExists(public_path('vendor/bootstrap-icons'));
File::copyDirectory($src, public_path('vendor/bootstrap-icons'));

Run it in the deploy script after composer install and before php artisan optimize. Then <link rel="stylesheet" href="{{ asset('vendor/bootstrap-icons/bootstrap-icons.min.css') }}">. For Mix, mix.copyDirectory('node_modules/bootstrap-icons/font/fonts', 'public/vendor/bootstrap-icons/fonts') alongside the Sass import is the equivalent.

Gotchas and Tips

  • Icon names are not stable across minor versions. v1.9.1 renamed cassette/cassette-fill; v1.10.0 renamed building → buildings. A composer update (or a floating ^1.10 in package.json) can silently blank out an icon in a template nobody retested. Pin the minor ("1.10.*"), commit the lockfile, and add a smoke test that renders every icon name in config/nav.php and asserts a non-empty <svg>.
  • Nothing is auto-discovered. No service provider means no vendor:publish --tag=icons; do not copy tutorials that assume one. Everything you get, you move yourself.
  • The font CSS is useless without font/fonts/. bootstrap-icons.css references ./fonts/bootstrap-icons.woff2 relatively. Copying only the CSS gives you tofu boxes (▯) and a 404 in the network tab. This is the single most common misconfiguration.
  • class="bi" on an inline SVG does nothing helpful. The bi class comes from the font stylesheet. When inlining, drop it and control size with width/height or CSS — otherwise you get display: inline-block and baseline gaps.
  • Never interpolate a raw {!! !!} icon name. The inner-SVG pattern above needs unescaped output by design; sanitise the name in the component constructor (as shown) and whitelist it in any FormRequest that accepts one.
  • Accessibility is not optional. Decorative icons need aria-hidden="true" and focusable="false" (older IE/Edge make SVGs tabbable otherwise). Icon-only buttons need their own label: <button aria-label="Delete"><x-icon name="trash" /></button>, or add <title> inside the SVG when the icon carries meaning on its own.
  • Mail and PDF are lossy. Most mail clients strip <svg>; dompdf's SVG support is partial at best. For notifications and invoices, either use a hosted PNG or accept text labels. Don't discover this after building an email template.
  • npm run icons / npm start are maintainer scripts. The repo is a Hugo documentation site. Cloning it and building is for contributing glyphs, not for consuming them — the npm/Composer packages ship pre-processed SVGs already run through SVGO.
  • Debugging a missing icon is almost always one of: wrong name (check icons/ directly), file read blocked by permissions in the container, the inner-SVG regex not matching because the file has an XML prolog, or a stale cache entry keyed only on the name. Include filemtime($file) in the cache key, as above, and php artisan view:clear when you swap the component implementation.
  • Extension points you actually control: the Blade component (attributes, default sizing, fallback glyph), the blade-icons set config (path/prefix/fallback), the icons/*.svg directory itself if you vendor a patched copy, and the CSS custom properties around currentColor on the font class. The package itself is intentionally dumb — "we only include the processed SVGs; it's up to you and your team to implement."
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.
vento/laravel-airbrake
wfeller/laravel-batch
maco-studios/mage-os-installer
busarm/armie
siberfx/laravel-logger
kfteam1991/dcat-admin
dashed/dashed-laposta
dashed/dashed-ecommerce-montaportal
dashed/dashed-ecommerce-etsy
neohq/string-blade-compiler
log1x/acf-composer
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager