tomshaw/laravel-gravatar
Laravel Gravatar 📸
Gravatar avatars, profiles, and QR codes for Laravel — zero config to start, configurable when you need it.
- Installation
- Rendering avatars — directive, component
- Building URLs — facade, fluent builder
- Models
- Profiles
- QR codes
- Hashing
- Testing
- Configuration
- Parameter reference
Installation
composer require tomshaw/laravel-gravatar
That's everything — the package auto-registers and every default is sensible. Publish the config only if you want to change them:
php artisan vendor:publish --tag=gravatar-config
Rendering avatars
The directive
@gravatar outputs a URL, so it goes inside a src attribute you write yourself:
<img src="https://raw.githubusercontent.com/tomshaw/laravel-gravatar/HEAD/@gravatar(email: 'email@example.com', size: 60, default: 'retro', rating: 'g')" />
Every parameter is named and optional except email.
The component
<x-gravatar /> emits the whole <img> tag. Because it owns the markup, it can do three things
the directive structurally cannot: forward your attributes, add a 2x srcset for high-density
displays, and swap in a local image when the browser can't reach Gravatar at all.
<x-gravatar email="{{ $user->email }}" :size="60" default="retro" class="rounded-full" />
<img src="https://secure.gravatar.com/avatar/2a53…8d6dd3.jpg?s=60&d=retro&r=g&f=n"
srcset="…?s=60&d=retro&r=g&f=n 1x, …?s=120&d=retro&r=g&f=n 2x"
class="rounded-full" width="60" height="60" />
| Prop | Default | Notes |
|---|---|---|
email |
— | Required. |
size |
config | Also sets width/height, which your own attributes override. |
default rating secure extension hash |
config | As per parameter reference. |
force-default |
false |
A real boolean here, rather than the directive's 'y'/'n'. |
retina |
true |
Emits the 2x srcset. Skipped automatically when doubling would exceed Gravatar's 2048px ceiling. |
fallback |
config | Local image swapped in via onerror. A relative path resolves through asset(). |
Note that default and fallback solve different problems. default is rendered by Gravatar
when an address has no avatar, so it needs Gravatar to be reachable. fallback is what the browser
shows when the request fails outright — a dropped connection, an outage, DNS.
Publish the view to change the markup:
php artisan vendor:publish --tag=gravatar-views
Building URLs
The facade
use TomShaw\Gravatar\Facades\Gravatar;
Gravatar::src('email@example.com', size: 200, default: 'retro');
Gravatar::hash('email@example.com');
Gravatar::profileUrl('email@example.com');
Gravatar::qrCode('email@example.com', size: 350);
Gravatar::has('email@example.com'); // bool — HTTP
Gravatar::profile('email@example.com'); // ?Profile — HTTP
Fluent builder
Gravatar::for($user->email)->size(200)->retro()->url();
Gravatar::for($user->email)->size(200)->rating('pg')->exists();
Each built-in default image is a shorthand — ->mp(), ->identicon(), ->monsterid(),
->wavatar(), ->retro(), ->robohash(), ->blank(), ->initials(), ->color().
The builder resolves to a URL when cast to a string, so it can be echoed straight into Blade:
<img src="{{ Gravatar::for($user->email)->size(120)->identicon() }}" />
Models
use TomShaw\Gravatar\Concerns\HasGravatar;
class User extends Authenticatable
{
use HasGravatar;
}
$user->gravatarUrl(size: 200, default: 'retro');
$user->gravatarProfileUrl();
$user->gravatarQrCode(size: 350);
$user->hasGravatar(); // HTTP
$user->gravatarProfile(); // HTTP
The address is read from the gravatar.email_column config value. Override it per-model:
protected function gravatarEmailColumn(): string
{
return 'contact_email';
}
Every method is prefixed deliberately. Eloquent resolves bare property access through relations and
attribute mutators, so a trait method named gravatar() would make $user->gravatar throw a
LogicException. Reach for Gravatar::for($user->email) when you want the fluent builder on a model.
Profiles
profile() returns a Profile, or null when the address has no Gravatar account.
$profile = Gravatar::profile('email@example.com');
$profile->displayName;
$profile->location;
$profile->jobTitle;
$profile->company;
$profile->pronouns;
$profile->verifiedAccounts;
$profile->get('timezone'); // any field, straight from the raw payload
$profile->toArray(); // the whole payload
This works with no API key. Gravatar withholds some fields from anonymous callers — timezone, languages, contact info, links, interests, payments, gallery images — and caps verified accounts at four. Set a key to receive full profiles and a higher rate limit:
GRAVATAR_API_KEY=your-key
Keys are free from gravatar.com/developers.
profile() and has() are the only methods that touch the network, so both cache — misses
included, so a missing profile doesn't re-request on every page load. Set gravatar.cache.ttl to
0 to disable.
QR codes
Gravatar::qrCode($user->email, size: 350, version: 3, type: 'gravatar');
version is 1 (standard) or 3 (modern dots). type is default (no logo), user (the user's
avatar in the centre), or gravatar (the Gravatar logo).
Hashing
Gravatar specifies SHA256 and explicitly says not to use MD5, which is reversible and can leak the address it was built from. This package defaults to SHA256.
MD5 stays available for URLs generated before the change:
Gravatar::src($email, hash: 'md5');
Gravatar::for($email)->md5()->url();
Or set gravatar.hash to md5 globally.
Testing
Gravatar::fake() answers has() and profile() from canned data and makes no HTTP calls. URL
building is left alone, since it never hit the network to begin with.
use TomShaw\Gravatar\Facades\Gravatar;
$fake = Gravatar::fake(
profiles: ['ada@example.com' => ['display_name' => 'Ada Lovelace']],
existing: ['grace@example.com'],
);
expect($user->gravatarProfile()->displayName)->toBe('Ada Lovelace');
expect(Gravatar::has('grace@example.com'))->toBeTrue();
expect(Gravatar::has('nobody@example.com'))->toBeFalse();
Emails in profiles also report as existing. Lookups are recorded:
$fake->assertLookedUp('ada@example.com');
$fake->assertNotLookedUp('someone@example.com');
$fake->assertNothingLookedUp();
Configuration
return [
'hash' => 'sha256', // or 'md5'
'size' => 80,
'default' => 'mp',
'rating' => 'g',
'secure' => true,
'extension' => 'jpg',
'fallback' => null, // local image for <x-gravatar />
'api_key' => env('GRAVATAR_API_KEY'),
'cache' => [
'store' => null, // null uses the app default
'ttl' => 3600, // 0 disables
'prefix' => 'gravatar',
],
'email_column' => 'email', // read by HasGravatar
];
Values passed at a call site always win over config.
Parameter reference
Accepted by src(), the directive, and (minus email) the component.
| Parameter | Type | Default | Accepts |
|---|---|---|---|
email |
string |
— | Required. |
size |
int |
80 |
1–2048. |
default |
string |
mp |
mp, identicon, monsterid, wavatar, retro, robohash, blank, 404, initials, color, or a publicly reachable URL. |
rating |
string |
g |
g, pg, r, x. |
secure |
bool |
true |
Uses secure.gravatar.com. |
forceDefault |
string |
n |
y always loads the default image. |
forceExtension |
string |
jpg |
jpg, jpeg, png, gif, webp, or '' for none. |
hash |
string |
sha256 |
sha256, md5. |
Anything outside these throws an InvalidArgumentException, so a typo fails loudly at render rather
than silently producing a dead URL.
Requirements
- PHP 8.5
- Laravel 13.0
Contributing
composer test # Pest
composer analyse # PHPStan
composer format # Pint
License
The MIT License (MIT). See License File for more information.