elfsundae/laravel-gravatar
elfsundae/laravel-gravatar is a lightweight Laravel integration for Gravatar. It builds avatar URLs from email addresses with configurable size, rating, default image, and secure hashing, letting you add consistent user avatars via a simple helper or facade in Blade views and PHP code.
composer require elfsundae/laravel-gravatar
The package is a thin wrapper around Gravatar's URL API — there is very little surface area, so a ten-minute skim of src/Gravatar.php, src/GravatarServiceProvider.php, and src/helpers.php on GitHub is enough to know the whole thing.
Where to look first
src/Gravatar.php – the core class. Expect a handful of methods: get()/url() (build the avatar URL), exists() (HEAD-check against Gravatar), and fluent setters for size, default, and rating.src/GravatarServiceProvider.php – shows whether it auto-registers (Laravel 5.5+ package:discover) and what alias/config it binds.config/gravatar.php (publish with php artisan vendor:publish --tag=gravatar) – global defaults for image size, fallback image, and rating.Register (only if not auto-discovered)
// config/app.php
'providers' => [
Elfsundae\LaravelGravatar\GravatarServiceProvider::class,
],
'aliases' => [
'Gravatar' => Elfsundae\LaravelGravatar\Facades\Gravatar::class,
],
First use case — a user avatar in Blade
<img src="{{ gravatar($user->email, 80) }}" alt="{{ $user->name }}">
or via the facade:
<img src="{{ Gravatar::get($user->email, 80) }}" alt="{{ $user->name }}">
That single line is the whole value proposition — you get a correctly hashed, query-string-built Gravatar URL without hand-writing md5(strtolower(trim($email))) and the ?s=&d=&r= parameters everywhere.
Model accessor (recommended over calling the helper at random)
Keep Gravatar logic in one place so you can swap it later (e.g. to a local upload or laravolt/avatar):
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function avatarUrl(): Attribute
{
return Attribute::get(fn () => Gravatar::get($this->email, 96));
}
<img src="{{ $user->avatar_url }}">
Fallback chain in Blade
@php($avatar = Gravatar::get($user->email, 64) ?: asset('img/default-avatar.png'))
<img src="{{ $avatar }}" loading="lazy" width="64" height="64">
Even though Gravatar is designed to always return an image (via the d= default), treat the URL as best-effort so a 404 from a corporate proxy never breaks the layout.
Default-image strategy
Gravatar's d parameter accepts identicon, monsterid, wavatar, retro, mm, 404, or a fully qualified URL. Point it at your own placeholder so brand consistency survives users who have no Gravatar:
Gravatar::setDefault(asset('img/avatar-placeholder.png'));
Using d=404 combined with Gravatar::exists() is the pattern for "show a real upload or a generated fallback" — but see the gotchas below.
Per-context sizing
Keep one default in config and override locally:
Gravatar::get($email, 32); // comment list
Gravatar::get($email, 200); // profile page
Because Gravatar URLs are deterministic per (email, size, default, rating), you can safely cache them in Redis or a DB column keyed on sha1($email . $size . $default . $rating) — the URL never changes for a given avatar.
Bulk rendering in a loop
For a list of 50 users, precompute once and reuse; don't call the helper twice per row (once in src and once in srcset):
$users->each(fn ($u) => $u->setAttribute('avatar', Gravatar::get($u->email, 48)));
Email normalization is the #1 bug source. Gravatar hashes md5(strtolower(trim($email))). If the package does not normalize for you, an email with stray whitespace or a capital letter silently yields a different (default) image. Verify in src/Gravatar.php and, if it doesn't trim/lowercase, wrap it:
Gravatar::get(strtolower(trim($email)));
exists() is an HTTP round-trip. It does a HEAD/GET against Gravatar and will block your request, and will happily fail on a network-restricted host. Never call it per row in a list view — batch it into a queued job and store a boolean, or skip it entirely and rely on d=identicon.
Use secure.gravatar.com. Gravatar's canonical host redirects; if the package hard-codes http://www.gravatar.com, you will get mixed-content warnings on HTTPS pages. Check and override the base URL if needed.
Laravel version pinning. This is a small, low-traffic package (38 stars, ~0.4 score) not under active development. Check composer.json's illuminate/* constraint before composer require — on Laravel 10/11 the service provider may fail to auto-discover or the facade alias may be stale. If it fights you, copy the ~30-line Gravatar class into your own app; the package is a convenience, not architecture.
Config caches are absolute. If you cached config (php artisan config:cache) after editing config/gravatar.php, Gravatar::setDefault() at runtime can be overridden by the cached value order — always call fluent setters after the service resolves, or better, keep the override local to the call site.
Rating defaults. The default r=g filters adult content and is usually what you want; don't "helpfully" bump it to x unless a product requirement demands it. Same for s= — Gravatar caps at 2048, and anything above returns a broken/oversized asset.
Extension point. If you need to swap providers or add srcset support, extend the facade root rather than editing vendor files:
Gravatar::macro('srcset', fn ($email) => collect([1, 2])
->map(fn ($dpr) => Gravatar::get($email, 96 * $dpr) . ' ' . $dpr . 'x')
->implode(', '));
Fallback when the package breaks: Gravatar is stable and the logic is tiny.
function gravatar(string $email, int $size = 80, string $default = 'identicon'): string
{
$hash = md5(strtolower(trim($email)));
return "https://secure.gravatar.com/avatar/{$hash}?" . http_build_query([
's' => $size, 'd' => $default, 'r' => 'g',
]);
}
Keep this in mind: for a package this small, your own helper is a legitimate, zero-dependency substitute if maintenance or Laravel compatibility becomes a concern.
How can I help you explore Laravel packages today?