outhebox/blade-flags
1,759 SVG country and language flags for Laravel Blade, Vue, React, and vanilla JS. Three flag styles, 128 language mappings, and regional variants like ar-sa, en-us, and fr-ca. Works with Inertia.js.
Installation:
composer require outhebox/blade-flags
For frontend frameworks (keep core and framework adapters on the same minor, e.g. all 0.3.x):
npm install @blade-flags/core@^0.3.0 @blade-flags/vue@^0.3.0 # Vue
npm install @blade-flags/core@^0.3.0 @blade-flags/react@^0.3.0 # React
First Use Case: Insert a country flag in a Blade view:
<x-flag-country-us />
Or a language flag:
<x-flag-language-en />
Where to Look First:
node_modules/@blade-flags/core/flags/ for the per-flag module paths shipped since 0.3.0Static Flags: Use predefined components for known flags:
<x-flag-circle-country-gb class="w-8 h-8" />
<x-flag-flat-language-es />
Dynamic Flags:
Use @svg directive for runtime-generated flag names:
@svg('flag-country-'.$user->country_code)
@svg('flag-circle-language-'.$user->language_code, ['class' => 'text-blue-500'])
Helper Method:
// In a controller or service
public function getFlagSvg($countryCode) {
return svg("flag-country-{$countryCode}")->toHtml();
}
Dynamic Rendering:
<script setup>
import { Flag } from '@blade-flags/vue'
import { circleFlags } from '@blade-flags/core/flags/circle'
</script>
<template>
<Flag :code="user.country" :flags="circleFlags" />
</template>
Static Import (single flag):
Since 0.3.0, every flag is its own module, so you can deep-import exactly one:
import countryUs from '@blade-flags/core/flags/circle/country-us'
import languageAr from '@blade-flags/core/flags/circle/language-ar'
Barrel imports (from '@blade-flags/core/flags/circle') still work, but pull the whole set into the graph.
Code-Split Loading (new in 0.3.0): The per-flag module layout makes a one-flag-per-chunk lazy load possible:
const loadFlag = (code) =>
import(`@blade-flags/core/flags/circle/country-${code}`)
.then(m => m.default)
const flag = await loadFlag(user.country)
Inertia.js: Pass flag data from Laravel to Vue/React via page props:
return Inertia::render('Profile', [
'user' => $user,
'flags' => ['country' => $user->country_code, 'language' => $user->language_code]
]);
Locale Switcher:
<div class="flex gap-2">
@foreach($availableLanguages as $lang)
<x-flag-circle-language-{{ $lang->code }} class="cursor-pointer"
wire:click="changeLanguage('{{ $lang->code }}')"/>
@endforeach
</div>
User Profile:
<div class="flex items-center gap-3">
<x-flag-country-{{ $user->country }} class="w-10 h-8" />
<span>{{ $user->name }}</span>
</div>
Admin Dashboard:
<table>
@foreach($users as $user)
<tr>
<td><x-flag-country-{{ $user->country }} /></td>
<td>{{ $user->name }}</td>
</tr>
@endforeach
</table>
CSS Sizing:
w-6 h-6 if hardcoded width/height exists in SVG.width="100%" height="100%" in SVG or wrap in a container with explicit dimensions.<div class="w-6 h-6">
<x-flag-country-us />
</div>
Missing Flags:
en-us instead of en) or fall back to a generic flag.Caching:
php artisan view:clear
php artisan cache:clear
Dynamic Flag Names:
$country->iso2_code or $language->code matches the package’s naming convention (e.g., us, en, ar-sa).resources/svg/ or use php artisan tinker to inspect:
\Outhebox\BladeFlags\Facades\BladeFlags::availableFlags();
Version Skew Between JS Packages (0.3.0):
@blade-flags/react and @blade-flags/vue 0.3.0 track the core version. Mixing [email protected] with an older adapter (or vice versa) can break flag resolution and the new per-flag module paths.package.json; check npm ls @blade-flags/core.
npm install @blade-flags/core@^0.3.0 @blade-flags/vue@^0.3.0
Bundler Handling of Variable Dynamic Imports:
import(\@blade-flags/core/flags/circle/country-${code}`)` is only statically analyzable up to the interpolation point. Vite/webpack build a context module for the whole directory, which can negate the chunking win or emit warnings.const flagModules = import.meta.glob('/node_modules/@blade-flags/core/flags/circle/country-*.js')
const loadFlag = (code) => flagModules[`/node_modules/@blade-flags/core/flags/circle/country-${code}.js`]()
code against an allow-list before interpolating into the specifier (untrusted input → failed chunk fetch at runtime).Inspect SVG Output:
{{ \Outhebox\BladeFlags\Facades\BladeFlags::svg('flag-country-us')->toHtml() }}
Look for malformed tags or missing attributes.
Check Published Assets: If using raw SVGs, verify they exist in:
public/vendor/blade-flags/country-us.svg
Frontend Debugging:
console.log(resolveFlag(circleFlags, 'us')); // Check if SVG is valid
const m = await import('@blade-flags/core/flags/circle/country-us')
console.log(Object.keys(m), m.default)
Language Overrides:
php artisan vendor:publish --tag=blade-flags-config
php artisan blade-flags:generate
Blade Icons Compatibility:
BladeIcon::resolve(), ensure the package is installed:
composer require blade-ui-kit/blade-icons
Inertia.js:
circleFlags) used in Blade for consistency.Custom Flags:
public/vendor/blade-flags/ and reference them via @svg('custom-flag').Flag Variants:
square-flags) by copying the build script and SVG templates.Dynamic Generation:
resolveFlag() helper to build flags programmatically:
$svg = \Outhebox\BladeFlags\Facades\BladeFlags::resolve('circle', 'country', 'us');
Deep Imports Over Barrel Imports (0.3.0):
import countryUs from '@blade-flags/core/flags/circle/country-us'
Lazy Loading / Code Splitting:
const loadFlag = async (code) => {
const { default: flag } = await import(`@blade-flags/core/flags/circle/country-${code}`)
return flag
}
Blade Caching:
// config/blade-flags.php
'cache' => env('APP_ENV') !== 'local',
Measure the Chunk Graph:
npx vite build --debug or webpack-bundle-analyzer — a single stray barrel import re-inlines the whole set.Combine with Blade Icons:
size and color:
<x-flag-country-us size="2x" color="#0000FF" />
Dark Mode:
<x-flag-country-us class="dark:invert" />
Accessibility:
aria-label for screen readers:
<x-flag-country-us aria-label="United States" />
Regional Variants:
- for regional flags (e.g., ar-sa for Arabic-Saudi Arabia), and mirror that naming in any dynamic import path:
<x-flag-circle-language-ar-sa />
Testing:
// Test invalid flag codes
$this->blade->render('@svg("flag-country-xx")')->assertSee('Invalid flag code');
code fails gracefully instead of throwing a chunk-load error:
await expect(loadFlag('xx')).rejects.toThrow();
How can I help you explore Laravel packages today?