forxer/laravel-gravatar
Laravel Gravatar makes it easy to generate Gravatar avatar URLs in Laravel apps, with configurable default images, sizes, ratings, and secure email hashing. Simple helpers and a facade keep avatar integration clean and flexible.
forxer/laravel-gravatar is a thin, focused wrapper around Gravatar URL generation — it does not download, cache, or transform images. Understand that boundary first: you are getting a correctly-hashed, correctly-signed URL, nothing more.
composer install forxer/laravel-gravatar
Package discovery registers the service provider and the Gravatar facade alias automatically, so there is nothing to add to config/app.php (important on Laravel 11+ skeletons, where the aliases array no longer exists).
Publish the config if you want to change defaults:
php artisan vendor:publish --tag=gravatar-config
Look at config/gravatar.php first — it holds the global defaults for size, rating, and default (fallback image), which every call inherits unless overridden.
Your first use case is almost always "user avatar in a Blade view":
<img src="{{ Gravatar::get($user->email) }}" alt="{{ $user->name }}" class="avatar">
That is the whole package in one line. Everything else in this assessment is about doing it well at scale.
4.4.0 note: this release only widens the framework constraint to allow Laravel 13. There are no API changes, no deprecations, and no config changes — an upgrade from 4.3.x is a composer update and nothing else.
Wrap it in a model accessor, not scattered through views. This is the single highest-leverage move: it gives you one place to swap to locally-stored avatars later.
public function getAvatarUrlAttribute(): string
{
return $this->avatar_path
? Storage::url($this->avatar_path)
: Gravatar::get($this->email, ['size' => 200, 'default' => 'mp']);
}
Choose a deliberate default strategy. The package default is a generic silhouette; for a consumer app, identicon, initials, or retro reads as "intentional" rather than "broken". If you want to know whether a user has a real Gravatar, that is a separate call (see Gotchas).
Centralise your size tokens. Gravatar's s parameter tops out at 2048. Pick two or three sizes (40 list, 96 card, 256 profile) and request 2× for retina displays rather than one large image scaled down everywhere — the CDN serves what you ask for, and asking for 256 in a 40px slot is pure waste.
Apply rating globally for user-generated content. Set rating in config to pg or r depending on your audience rather than remembering it per call site. For a kids' product, g.
Include it in API resources. Keep the URL generation server-side so mobile clients don't reimplement hashing:
return [
'id' => $this->id,
'name' => $this->name,
'avatar_url' => $this->avatar_url,
];
Cache the generated URL with the user record. Hashing is cheap, but the accessor is called on every serialisation. Since the URL is a pure function of email + options, Cache::remember("avatar:{$this->id}", ...) or simply storing it in the model on save works well.
Use it for previews in admin/CRM screens. An email input plus a live <img> preview on blur is a genuinely nice touch and costs one call.
Test by asserting the URL, not mocking the package. Because the output is deterministic, assertStringContainsString('gravatar.com/avatar/', $user->avatar_url) is usually better than a facade mock.
Never pass an already-hashed email. The package MD5-hashes what you give it, per the Gravatar spec (trimmed, lowercased). If you're migrating from a system that stored md5($email) and pass that value in, you'll get a hash of a hash and silent 404s. Pass the raw email and let the package normalise it.
exists() is a live network call — treat it as such. It relies on Gravatar's d=404 behaviour to detect a missing avatar. That means: no caching, no batching, a round-trip per user, and it will tank a listing page. If you use it at all, do it once at registration or in a queued job, and persist the result. Also note Laravel's Http::fake() won't intercept it if the underlying implementation uses get_headers(), so mock the facade in tests instead of faking HTTP.
Beware the N+1 of avatars. A 50-row user table generates 50 image requests from the browser. That's usually acceptable (they're CDN-served and parallel), but in email templates or PDF generation it isn't — those clients often block remote images entirely, so always render a text fallback (initials) alongside.
Hashed email is still PII. MD5 of an email address is trivially reversible for common domains, and sending it to a third party is a GDPR consideration in the EU. If that matters, proxy through your own endpoint or store avatars locally at signup. The package gives you no privacy controls — that decision is yours.
Config caching bites if you read env at runtime. config('gravatar.size') is fine under php artisan config:cache, but don't call env() in your own wrapper code.
default is not fallback. Passing default => 'https://cdn.example.com/fallback.png' requires that URL to be publicly reachable by Gravatar's servers, not by you. A localhost or auth-gated URL silently yields a broken image.
Laravel 13 / PHP version alignment. 4.4.0 adds Laravel 13 support on the 4.x branch, so 4.x is now the line to stay on across Laravel 10→13. If you pinned "forxer/laravel-gravatar": "4.3.*" you won't pick this up — loosen to ^4.4. If you're still on a 3.x constraint, moving to 4.x is where the breaking changes live (PHP and framework minimums), not here.
No image processing, no Html helper, no Blade component ships with it. If you want <x-avatar> you're writing it; the package is deliberately just the URL layer. That's a feature — it keeps the dependency upgrade-safe, which 4.4.0 demonstrates.
How can I help you explore Laravel packages today?