multiavatar/multiavatar-php
Multiavatar PHP generates unique, deterministic SVG avatars from any string. It bundles the full Multiavatar SVG dataset and returns ready-to-use markup, with no API calls or image processing required, making it easy to add colorful user avatars to Laravel or plain PHP applications.
Install with Composer — the package is a plain library, so there is no service provider, no config file to publish, and nothing to register in config/app.php:
composer require multiavatar/multiavatar-php
The first place to look is the class itself (src/Multiavatar.php or src/Multiavatar/Multiavatar.php depending on the version). It contains one callable method, __invoke(string $seed): string, plus a set of large arrays of SVG path fragments (backgrounds, hair, eyes, mouth, clothing, etc.). That file is the whole package.
Minimal usage:
use Multiavatar\Multiavatar;
$svg = (new Multiavatar())('[email protected]');
// $svg is a complete <svg …>…</svg> string
The generator hashes the seed string, splits the hash into indices, and picks one fragment per feature group. Same seed always yields the same avatar, on any server, with no storage. That determinism is the entire value proposition.
First use case: a fallback avatar for users who haven't uploaded a profile picture. Drop it into an existing accessor or a Blade component and you're done — you don't need a database column, a filesystem, or a job.
// User model
public function getAvatarSvgAttribute(): string
{
return (new Multiavatar())($this->email ?: (string) $this->id);
}
Blade component (inline SVG). Inlining keeps the avatar styleable with CSS and avoids a second HTTP request:
// app/View/Components/Avatar.php
class Avatar extends Component
{
public function __construct(public string $seed, public string $class = '') {}
public function render()
{
return view('components.avatar', ['svg' => (new Multiavatar())($this->seed)]);
}
}
{{-- resources/views/components/avatar.blade.php --}}
<div class="avatar {{ $class }}" aria-hidden="true">{!! $svg !!}</div>
Dedicated SVG route (better for caching and CDNs). Serve a real image URL so browsers, Cloudflare, and Laravel's response cache can do their job:
Route::get('/avatar/{seed}.svg', function (string $seed) {
$svg = Cache::remember("avatar:v1:{$seed}", now()->addWeek(),
fn () => (new Multiavatar())($seed));
return response($svg, 200, [
'Content-Type' => 'image/svg+xml',
'Cache-Control' => 'public, max-age=604800, immutable',
]);
})->where('seed', '[A-Za-z0-9._-]{1,64}');
Constrain the route parameter, as above — don't let arbitrary bytes become cache keys.
Data URIs for email and PDF. Email clients are hostile to SVG, and DOM PDF libraries often can't fetch routes. Embed instead:
$src = 'data:image/svg+xml;base64,' . base64_encode((new Multiavatar())($user->email));
<img src="{{ $src }}"> works in most modern email clients, though Outlook desktop will still refuse SVG — fall back to a PNG in that case.
Persist at registration if you need an immutable avatar. Generate once and store it, so a later seed change (email change, or a package upgrade that alters the palette) doesn't silently redraw everyone:
Storage::disk('public')->put("avatars/{$user->id}.svg", (new Multiavatar())($user->email));
Seeding. In factories and seeders, feed fake()->email() — you get a visually stable fixture set that looks realistic in screenshots and demos, with no external image service.
"Regenerate" affordance. Since the seed is the only input, salting it gives you a new avatar on demand: (new Multiavatar())($email . $nonce). Store the nonce, not the SVG, if you care about disk.
"Alice", "alice ", and " alice" are three different avatars. Normalize once, before you call it: mb_strtolower(trim($seed)), and pick either email or ID consistently. Otherwise a user's avatar will flip when they edit their profile.mb_strtolower on a UTF-8 name may not collapse case the way you expect. For anything user-facing, hash a stable ASCII fallback (email or ID) rather than a display name.if (! $seed) at the call site if a blank avatar is not what you want.composer.json ("multiavatar/multiavatar-php": "1.*") and bump deliberately, then purge the avatar cache.avatar:{seed}, include a version token in the key (avatar:v2:{seed}) so a palette change isn't masked by stale cache entries.Content-Type. image/svg+xml, not text/html. For Content-Disposition: attachment downloads, use .svg and consider X-Content-Type-Options: nosniff.{{ }}. Never inline user-supplied SVG through the same {!! !!}.<svg> is fine under img-src/style-src rules, but data: URIs need img-src data: in your policy. If you're on a strict CSP, the dedicated route pattern is the safer integration.$this->assertSame((new Multiavatar())('x'), (new Multiavatar())('x'));. Avoid snapshotting the full SVG unless you want a test failure on every package bump.How can I help you explore Laravel packages today?