Build and use the elfeffe/image-resizer package for optimized media URLs, responsive HTML, BlurHash/LQIP generation, and package image delivery.
php artisan boost:add-skill elfeffe/image-resizer
Save this content to: .claude/skills/image-resizer-development/SKILL.md
# Image Resizer Development
Use this skill when working on `elfeffe/image-resizer`, optimized media output, responsive image markup, or package image delivery routes.
## Main package pieces
| Class / file | Purpose |
|---|---|
| `Traits\HasImageResizer` | Model helpers for URLs, HTML, and srcsets |
| `Http\Controllers\ImageResizerController` | Dynamic resize endpoint |
| `Support\Access` | Signature, cache folder, type and size limits |
| `Jobs\CalculateLqipJob` | Generates BlurHash and LQIP data |
| `Jobs\WarmResizesJob` | Makes the resizes a model's `imageResizerWarmUrls()` lists, right after upload |
| `routes/web.php` | Registers `image_resizer/...` route |
| `Console\Commands\InstallHtaccessCommand` | Apache optimization helper |
## Core usage
Add the trait to a model that already uses Spatie Media Library:
```php
use Elfeffe\ImageResizer\Traits\HasImageResizer;
class Product extends Model implements HasMedia
{
use HasImageResizer;
use InteractsWithMedia;
}
```
Common helpers:
```php
$url = $model->getMediaUrl($media, 800, 600);
$html = $model->getMediaHtml($media, 800, 600, 'resize', ['alt' => 'Description']);
$srcset = $model->getMediaSrcset($media, [400, 600, 800, 1200]);
```
## Frontend directives
Include the package directives once in the layout:
```blade
@imageResizerStyles
@imageResizerScripts
```
## Route, storage, and serving modes
- The package serves images through `/image_resizer/{id}/w/{w}/h/{h}/{type}/{path}.{ext}`.
- Storage is configured in `config/image-resizer.php` (env prefix `IMAGERESIZER_*`):
- `storage.driver=local` (default): cached files at `{id}/{w}x{h}/{type}.{ext}` on the local `image_resizer` disk; web-server fast paths apply (`image-resizer:install-htaccess`).
- `storage.driver=s3`: resizes live in the bucket under `storage.root` (default `image_resizer`); no local writes.
- `serve.mode=cdn_origin` + `serve.url`: URLs point at the CDN, which origin-pulls the app (GET-first on the bucket, generate on miss, stream; never redirects).
- `serve.mode=redirect` + `serve.url`: app route 301s to the public object URL (requires public-read bucket).
- `type=original` redirects to the original media URL, and is a 403 unless `image-resizer.allow_original` is on.
- Access control (`Support\Access`, `ImageResizerController::admit()`): a project that sets neither `image-resizer.signed` nor `image-resizer.strict` is served exactly as before; only the route's own segments are read (a query-string `type`/`img` never overrides the route). A strict project (or one that signs): `w`/`h` above `image-resizer.max_dimension` and a `type` outside `Access::TYPES` are a 422, `original` a 403 unless `image-resizer.allow_original`, `fit` never above the source. `image-resizer.throttle` (unset = off) sets the named limiter `image-resizer` (429). With `image-resizer.signed` every builder URL ends in `?s=` (HMAC of `{id}|{w}|{h}|{type}|{name}.{ext}`), the route answers 403 to any other request before it reads anything, and the cache lives under `Access::folder()`'s token. Never build a resizer URL by hand. `webwizo/laravel-shortcodes` is optional (guard with `class_exists` and `app()->bound('shortcode')`); `image-resizer.prune.schedule` false lets a host queue `image-resizer:prune` itself.
- Objects are stored with `Content-Type` and `Cache-Control: public, max-age=2628000`; storage failures log + 500 (never silent).
## Preparing resizes
- A model opts in with `public function imageResizerWarmUrls(Media $media): array` (the URLs its views ask for, from `getMediaUrl()`); `WarmResizesJob` is queued when its media is created and makes them from one read of the original. `image-resizer:warm [--model=*]` queues it for existing media.
- `ImageResizerController::warm()` resolves each URL through the route and `admit()`, so signatures and canonical spelling are those of a real request.
## Best practices
- Prefer `getMediaHtml()` when you want package lazy-loading and presentation behavior
- Prefer package helpers instead of hand-building resized image URLs
- Keep parent containers dimensioned when the UI depends on predictable image layout
- Search Laravel, Spatie Media Library, and package docs before changing image generation or delivery behavior
Use this skill when working on elfeffe/image-resizer, optimized media output, responsive image markup, or package image delivery routes.
| Class / file | Purpose |
|---|---|
Traits\HasImageResizer |
Model helpers for URLs, HTML, and srcsets |
Http\Controllers\ImageResizerController |
Dynamic resize endpoint |
Support\Access |
Signature, cache folder, type and size limits |
Jobs\CalculateLqipJob |
Generates BlurHash and LQIP data |
Jobs\WarmResizesJob |
Makes the resizes a model's imageResizerWarmUrls() lists, right after upload |
routes/web.php |
Registers image_resizer/... route |
Console\Commands\InstallHtaccessCommand |
Apache optimization helper |
Add the trait to a model that already uses Spatie Media Library:
use Elfeffe\ImageResizer\Traits\HasImageResizer;
class Product extends Model implements HasMedia
{
use HasImageResizer;
use InteractsWithMedia;
}
Common helpers:
$url = $model->getMediaUrl($media, 800, 600);
$html = $model->getMediaHtml($media, 800, 600, 'resize', ['alt' => 'Description']);
$srcset = $model->getMediaSrcset($media, [400, 600, 800, 1200]);
Include the package directives once in the layout:
@imageResizerStyles
@imageResizerScripts
/image_resizer/{id}/w/{w}/h/{h}/{type}/{path}.{ext}.config/image-resizer.php (env prefix IMAGERESIZER_*):
storage.driver=local (default): cached files at {id}/{w}x{h}/{type}.{ext} on the local image_resizer disk; web-server fast paths apply (image-resizer:install-htaccess).storage.driver=s3: resizes live in the bucket under storage.root (default image_resizer); no local writes.serve.mode=cdn_origin + serve.url: URLs point at the CDN, which origin-pulls the app (GET-first on the bucket, generate on miss, stream; never redirects).serve.mode=redirect + serve.url: app route 301s to the public object URL (requires public-read bucket).type=original redirects to the original media URL, and is a 403 unless image-resizer.allow_original is on.Support\Access, ImageResizerController::admit()): a project that sets neither image-resizer.signed nor image-resizer.strict is served exactly as before; only the route's own segments are read (a query-string type/img never overrides the route). A strict project (or one that signs): w/h above image-resizer.max_dimension and a type outside Access::TYPES are a 422, original a 403 unless image-resizer.allow_original, fit never above the source. image-resizer.throttle (unset = off) sets the named limiter image-resizer (429). With image-resizer.signed every builder URL ends in ?s= (HMAC of {id}|{w}|{h}|{type}|{name}.{ext}), the route answers 403 to any other request before it reads anything, and the cache lives under Access::folder()'s token. Never build a resizer URL by hand. webwizo/laravel-shortcodes is optional (guard with class_exists and app()->bound('shortcode')); image-resizer.prune.schedule false lets a host queue image-resizer:prune itself.Content-Type and Cache-Control: public, max-age=2628000; storage failures log + 500 (never silent).public function imageResizerWarmUrls(Media $media): array (the URLs its views ask for, from getMediaUrl()); WarmResizesJob is queued when its media is created and makes them from one read of the original. image-resizer:warm [--model=*] queues it for existing media.ImageResizerController::warm() resolves each URL through the route and admit(), so signatures and canonical spelling are those of a real request.getMediaHtml() when you want package lazy-loading and presentation behaviorHow can I help you explore Laravel packages today?