shammaa/laravel-smart-glide

Advanced image processing toolkit for Laravel with unified /img routing, responsive components, and intelligent caching.

Maintainers

Package info

github.com/shammaa/laravel-smart-glide

pkg:composer/shammaa/laravel-smart-glide

Statistics

Installs: 57

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 0

v2.3.0 2026-03-29 19:18 UTC

This package is auto-updated.

Last update: 2026-03-29 19:18:27 UTC


README

Advanced image processing toolkit for Laravel with unified /img routing, responsive components, intelligent caching, and a headless JSON API for React/Next.js/Vue frontends.

Features

  • πŸ” Signed URLs & Validation β€” Protect image transformations with HMAC signatures, strict parameter validation, and extension whitelisting.
  • πŸ–ΌοΈ Responsive Components β€” Blade components generate srcset, sizes, and background media queries automatically.
  • βš™οΈ Smart Profiles β€” Define reusable WebP/AVIF compression presets and override them from an external PHP profile file.
  • πŸ“¦ Unified Delivery Path β€” All image responses flow through /img/..., simplifying CDNs, caching rules, and observability.
  • 🧠 Intelligent Caching β€” LRU-inspired cache manifest with size budgets, warmup metadata, and background eviction.
  • 🧾 SEO-Ready β€” Configurable attributes (loading, fetchpriority, aria, title, etc.) plus optional JSON-LD ImageObject snippets.
  • πŸ›‘οΈ Security Rules β€” Block remote sources (optional), enforce max width/height/quality ranges, and stricter MIME filters.
  • 🧰 DX Friendly β€” Publishable config, auto-discovered components, and clean service provider wiring.
  • 🌐 Headless JSON API β€” /img-data endpoint returns src, srcset, sizes, and blurDataUrl as JSON for React/Vue/Mobile consumers.
  • πŸ› οΈ Admin Management API β€” HTTP endpoints to warm, forget, and inspect cached images directly from your dashboard.
  • πŸ“Š Cache Introspection β€” Check image existence, read dimensions, and view cache statistics through the Facade or HTTP.

Requirements

  • PHP 8.3+
  • Laravel 11.x or 12.x
  • GD or Imagick PHP extension
  • League Glide 3.x (installed automatically)

Installation

composer require shammaa/laravel-smart-glide

The service provider and components are auto-discovered. If discovery is disabled, register the provider manually in bootstrap/app.php (Laravel 11+):

return Application::configure(basePath: dirname(__DIR__))
    ->withProviders([
        Shammaa\SmartGlide\SmartGlideServiceProvider::class,
    ])
    ->create();

Publish the configuration file when you want to customize paths, caching, or SEO defaults:

php artisan vendor:publish --tag=smart-glide-config

This creates config/smart-glide.php in your application.

Environment Setup

  1. Signing keys
    APP_KEY=base64:...
    # Optional dedicated key
    SMART_GLIDE_SIGNATURE_KEY=base64:...
    After modifying the .env file, always clear cached configuration:
    php artisan config:clear
    php artisan cache:clear
  2. Filesystem
    • Originals default to storage/app/public.
    • Processed images live in storage/app/smart-glide-cache.
    • Run php artisan storage:link once if your front-end needs to serve the originals.
  3. Permissions Make sure the cache directory is writable by your web server user.

Quick Start

  1. Prepare assets
    Place an image inside storage/app/public (default source path). If you need public access to the originals, create the symlink once via php artisan storage:link.
  2. Drop the component
    <x-smart-glide-img src="team/photo.jpg" alt="Team portrait" width="960" height="640" />
  3. Load the page
    The rendered <img> tag now contains a fully signed src, responsive srcset, sizes, and fixed width/height for layout stability. All requests resolve to /img/team/photo.jpg?....
  4. Need a different source path?
    Edit config/smart-glide.php ('source' => ...) and clear configuration cache:
    php artisan config:clear
    php artisan cache:clear

Verify in Tinker

php artisan tinker
>>> app(Shammaa\SmartGlide\Support\SmartGlideManager::class)->deliveryUrl('team/photo.jpg', ['w' => 640])

Opening the returned URL in a browser should yield the processed image (HTTP 200). A 403 usually indicates an invalid or missing signatureβ€”double‑check your APP_KEY/SMART_GLIDE_SIGNATURE_KEY and clear cached config.

Facade Usage

Resolve URLs anywhere in your Laravel app through the SmartGlide facade. This is convenient for tables, JSON APIs, or Livewire components where you need pre-signed image URLs without rendering Blade components.

use Shammaa\SmartGlide\Facades\SmartGlide;

$url = SmartGlide::croppedUrl(
    path: 'team/photo.jpg',
    width: 160,
    height: 160,
    parameters: [
        'profile' => 'thumbnail', // merges preset params (quality, format, etc.)
        'focus' => 'faces',       // any additional Glide parameters
    ],
);

In a DataTable JSON feed:

return TeamMember::query()
    ->select(['id', 'name', 'photo_path'])
    ->get()
    ->map(fn ($member) => [
        'id' => $member->id,
        'name' => $member->name,
        'photo' => SmartGlide::croppedUrl($member->photo_path, 96, 96, [
            'profile' => 'thumbnail',
        ]),
    ]);

Then render it in JavaScript:

$('#team-table').DataTable({
    columns: [
        { data: 'name' },
        {
            data: 'photo',
            render: (data, type, row) => type === 'display'
                ? `<img src="${data}" alt="${row.name}" class="h-12 w-12 rounded-full object-cover" />`
                : data,
            orderable: false,
            searchable: false,
        },
    ],
});

Headless & API Usage (React / Vue / Mobile)

JSON Data API

Smart Glide provides a dedicated JSON endpoint at /img-data/{path} for headless frontends. This returns all responsive data as a structured JSON payload β€” ideal for React, Vue, Next.js, or mobile apps.

GET /img-data/products/phone.jpg?profile=hero&responsive=retina&blur_placeholder=1&schema=1

Response:

{
  "src": "/img/products/phone.jpg?profile=hero&s=...",
  "srcset": "/img/products/phone.jpg?w=640&... 640w, /img/products/phone.jpg?w=960&... 960w, ...",
  "sizes": "(max-width: 640px) 100vw, (max-width: 960px) 100vw, ...",
  "widths": [640, 960, 1280, 1920, 2560],
  "blurDataUrl": "data:image/webp;base64,UklGR...",
  "schema": {
    "@context": "https://schema.org",
    "@type": "ImageObject",
    "contentUrl": "https://example.com/img/products/phone.jpg?..."
  }
}

Query Parameters:

Parameter Type Description
profile string Named compression profile (hero, thumbnail, etc.)
responsive string Named responsive set, comma-separated widths, or 0 to disable
blur_placeholder boolean Include base64 LQIP data URI
schema boolean Include JSON-LD ImageObject data
alt string Alt text used in the schema caption
w, h, fit, focus, q, fm mixed Any additional Glide transformation parameters

Facade: responsiveData()

Extract responsive data as a plain array from PHP:

use Shammaa\SmartGlide\Facades\SmartGlide;

// In a Laravel Controller or Resource
$imageData = SmartGlide::responsiveData(
    path: 'products/phone.jpg',
    profile: 'thumbnail',
    responsive: 'retina' // or [320, 640, 960]
);

return response()->json([
    'product_title' => 'Awesome Phone',
    'image' => $imageData
]);

Facade: apiPayload()

Build the complete JSON-ready payload (including blur placeholder and dimensions) from the Facade β€” perfect for Inertia.js props and Laravel API Resources:

use Shammaa\SmartGlide\Facades\SmartGlide;

// In a Laravel Resource or Inertia page
return SmartGlide::apiPayload('products/phone.jpg', [
    'profile'          => 'hero',
    'responsive'       => 'retina',
    'blur_placeholder' => true,
    'dimensions'       => true,
]);

// Returns: { src, srcset, sizes, widths, blurDataUrl, dimensions: { width, height } }

Consumption in React/Vue

Simply bind the returned attributes to your <img> tag:

// React / Vue Example
<img 
  src={image.src} 
  srcSet={image.srcset} 
  sizes={image.sizes} 
  alt={title}
  loading="lazy"
/>

For a full-featured React/Next.js SDK, install the companion package smart-glide-react (npm install smart-glide-react) which provides <SmartGlideImage>, hooks, and Next.js loader integration.

Image Introspection

Check Image Existence

if (SmartGlide::imageExists('avatars/user-42.jpg')) {
    // Image found in source directory
}

Get Original Dimensions

$dims = SmartGlide::dimensions('hero.jpg');
// ['width' => 3840, 'height' => 2160]

Generate Multiple URLs

$urls = SmartGlide::multipleUrls('hero.jpg', [640, 960, 1280], ['profile' => 'hero']);
// [640 => '/img/hero.jpg?w=640&...', 960 => '/img/hero.jpg?w=960&...', ...]

Admin Management API

Enable HTTP endpoints for managing the image cache directly from your website or admin dashboard.

Setup

SMART_GLIDE_ADMIN_ENABLED=true
SMART_GLIDE_ADMIN_PATH=/img-admin

Security: Admin routes are protected by auth middleware by default. Configure this in config/smart-glide.php under admin.middleware.

Endpoints

Cache Statistics

GET /img-admin/stats
{
  "count": 152,
  "size_mb": 48.3,
  "cache_path": "/var/www/storage/app/smart-glide-cache",
  "source_path": "/var/www/storage/app/public",
  "max_size_mb": 1024
}

Full Manifest

GET /img-admin/stats/manifest

Returns every cached rendition with path, width, profile, format, and timestamps.

Warm a Single Image

POST /img-admin/warm
Content-Type: application/json

{
  "path": "products/phone.jpg",
  "profile": "hero",
  "widths": [640, 960, 1280]
}
{ "warmed": true, "path": "products/phone.jpg", "widths": [640, 960, 1280] }

Warm All Source Images

POST /img-admin/warm-all
Content-Type: application/json

{
  "profile": "thumbnail",
  "widths": [320, 640],
  "extensions": ["jpg", "png"]
}
{ "warmed_count": 48, "skipped_count": 3, "size_mb": 52.1, "total_entries": 192 }

Evict Cache for an Image

Call this after replacing or updating the original file:

POST /img-admin/forget
Content-Type: application/json

{ "path": "products/phone.jpg" }
{ "forgotten": true, "path": "products/phone.jpg", "deleted_count": 5 }

Check Image Existence

GET /img-admin/exists?path=products/phone.jpg
{ "exists": true, "path": "products/phone.jpg" }

Get Image Dimensions

GET /img-admin/dimensions?path=products/phone.jpg
{ "path": "products/phone.jpg", "width": 3840, "height": 2160 }

Cache Management (PHP)

Warm Paths Programmatically

Pre-process and cache image renditions (ideal for queued jobs):

// Warm specific widths
SmartGlide::warmPath('products/phone.jpg', [320, 640, 960, 1280], ['profile' => 'hero']);

// Warm using config breakpoints
SmartGlide::warmPath('hero.jpg');

Forget (Purge) a Path

Delete all cached renditions after replacing the original file:

$deleted = SmartGlide::forgetPath('products/phone.jpg');
// Returns count of deleted entries (e.g. 5)

Cache Statistics

$stats = SmartGlide::cacheStats();
// ['count' => 152, 'size_mb' => 48.3, 'manifest' => [...]]

Configuration Overview

The config file (config/smart-glide.php) exposes several groups:

Key Description
source, cache, delivery_path Control filesystem paths and the unified /img endpoint.
security Toggle URL signing, allowed formats, max dimensions, quality ranges, and remote sources.
profiles & profile_file Define reusable transformation presets; merge an external PHP file at runtime.
breakpoints Global widths for automatic srcset generation.
responsive_sets Named breakpoint aliases usable from the components.
cache_strategy Enforce cache size (MB) and LRU time window.
cache_headers Configure browser cache days and enable ETag responses.
logging Enable processing logs and choose the log channel.
seo Default attributes for <img> / background components and structured data behaviour.
api Enable/disable the headless JSON API endpoint and its middleware.
admin Enable/disable admin management endpoints, set path and auth middleware.

Tip: set SMART_GLIDE_SCHEMA_ENABLED=true to emit JSON-LD ImageObject snippets for every component by default.

Default Profiles & Responsive Sets

Profiles (config('smart-glide.profiles'))

Name Description
default Outputs WebP at quality 82.
thumbnail 320Γ—320 crop for avatars or cards.
hero 1600Γ—900 centered crop for hero banners.
portrait 800Γ—1200 crop prioritising faces.
square 600Γ—600 crop for grids.
profile_photo 400Γ—400 crop focused on faces (avatars).
cover 2048Γ—1152 crop suitable for cover art.
background 2560Γ—1440 max-fit background-friendly rendition.

Use them via profile="hero" or override/add more in your config file.

Responsive sets (config('smart-glide.responsive_sets'))

Name Widths
hero 640, 960, 1280, 1600, 1920
thumbnails 240, 320, 480
square 320, 480, 640
portrait 480, 768, 1024
hd 960, 1280, 1600
fhd 1280, 1600, 1920, 2560
retina 640, 960, 1280, 1920, 2560

Reference them with responsive="retina" or specify an inline array.

Blade Components

<x-smart-glide-img>

Render responsive <img> tags with minimal boilerplate.

<x-smart-glide-img
    src="portfolio/hero.jpg"
    profile="hero"
    alt="Product hero image"
    class="rounded-xl shadow-lg"
    style="display:block"
    aspect-ratio="16:9"
    :params="['fit' => 'crop', 'focus' => 'center']"
    :seo="[
        'fetchpriority' => 'high',
        'title' => 'Hero Image',
        'data-license' => 'CC-BY-4.0',
    ]"
    schema
/>

Common properties

Prop Type Description
src string Relative path (inside smart-glide.source).
profile string Apply a config profile (profiles.hero).
params array Additional Glide parameters (width, height, etc.).
style string Inline styles applied to <img>.
aspect-ratio string Aspect ratio hint (e.g. 16:9, 1.5).
width / height int Native dimension attributes for layout stability.
seo array Extra HTML attributes (fetchpriority, title, itemprop, …).
schema bool Force JSON-LD emission for this image only.

Structured data respects component-level overrides and merges extra fields from config('smart-glide.seo.structured_data.fields').

View Overrides & Fallback Rendering

When you publish the package views, make sure resources/views/vendor/smart-glide/components/img.blade.php is kept in sync with the version shipped by the package. If it is removed or left empty, the component now falls back to rendering the <img> tag inline, so URLs remain signed even with an incomplete override. You can safely delete the override to restore the default template.

How srcset Is Generated

  • Smart Glide reads breakpoint widths from config('smart-glide.breakpoints') by default.
  • For each width, the component clones your parameters, overrides w, and calls the delivery URL to produce entries like /img/photo.jpg?w=640&... 640w.
  • The generated HTML will look similar to:
    <img
        src="/img/photo.jpg?w=960&fm=webp&q=82&s=..."
        srcset="/img/photo.jpg?w=360&... 360w, /img/photo.jpg?w=640&... 640w, ..."
        sizes="(max-width: 360px) 100vw, (max-width: 640px) 100vw, ..."
    >
  • Update the breakpoints globally by editing config('smart-glide.breakpoints'), or define named presets in responsive_sets for reuse (responsive="hero").
  • Override widths without touching the config:
    <x-smart-glide-img src="gallery/piece.jpg" :responsive="[320, 640, 960]" />
  • Use a named preset from config('smart-glide.responsive_sets'):
    <x-smart-glide-img src="hero.jpg" responsive="hero" />
  • Disable srcset entirely when you want a single rendition:
    <x-smart-glide-img src="logo.png" :responsive="false" />
  • If you need full control, set the attributes manually via :seo="['sizes' => '...']" or ['srcset' => '...'].

<x-smart-glide-bg>

Responsive background helper with media queries and placeholders.

<x-smart-glide-bg
    src="banners/summit.jpg"
    profile="hero"
    class="hero-banner"
    alt="Annual summit banner"
    lazy="true"
    :seo="['data-license' => 'Internal use only']"
    responsive="hero"
/>

The component outputs a wrapper <div> with inline styles plus an optional <style> block for breakpoint-specific backgrounds. A blurred placeholder overlay is included when lazy is true.

<x-smart-glide-picture>

Responsive <picture> helper that lets you define multiple <source> breakpoints and a Smart Glide powered fallback.

<x-smart-glide-picture
    src="gallery/feature.jpg"
    alt="Feature image"
    class="feature-picture"
    aspect-ratio="1:1"
    :sources="[
        ['media' => '(min-width: 1200px)', 'widths' => [1200], 'params' => ['fit' => 'crop', 'w' => 1200, 'h' => 675]],
        ['media' => '(min-width: 768px)', 'widths' => [900], 'params' => ['w' => 900, 'h' => 506]],
    ]"
/>

Each source entry accepts:

Key Description
media Optional media query for the <source> tag.
src Override image path for this source (defaults to component src).
params Glide parameters merged with the base parameters.
profile Profile name to merge with parameters.
widths / responsive Array or preset used to generate the srcset.
sizes Custom sizes attribute.
type MIME type hint for the source.
srcset Custom array of descriptors if you need full control.
img-class / img-style Styling for the fallback <img>.
img-width / img-height Native width/height attributes on fallback <img>.
width / height Set attributes on the wrapping <picture>.
aspect-ratio Sets CSS aspect-ratio on fallback <img>.

The internal <img> fallback inherits Smart Glide features (SEO attributes, structured data, signed URLs) and can be styled separately via img-class.

Picture Presets

Skip repeating :sources="[]" arrays in every template by defining presets in config('smart-glide.picture_presets'):

'picture_presets' => [
    'feature' => [
        ['media' => '(min-width: 1200px)', 'widths' => [1200], 'params' => ['fit' => 'crop', 'w' => 1200, 'h' => 675]],
        ['media' => '(min-width: 768px)', 'widths' => [900], 'params' => ['fit' => 'crop', 'w' => 900, 'h' => 506]],
    ],
],

Use the preset in Blade:

<x-smart-glide-picture
    preset="feature"
    src="{{ $article->featured_image }}"
    alt="{{ $article->title }}"
    aspect-ratio="16:9"
/>

You can still pass :sources="[]" along with a preset; they will be appended for page-specific overrides.

Signing & Security

  • URLs are signed with HMAC using SMART_GLIDE_SIGNATURE_KEY or APP_KEY when security.secure_urls is enabled.
  • Parameters are sanitized and validated against maximum width/height and quality limits.
  • Remote image sources are rejected by default; enable them per environment with SMART_GLIDE_ALLOW_REMOTE=true.
  • Allowed output formats (fm) and file extensions are configurable to avoid unsafe file types.

Requests with invalid signatures or disallowed parameters return 403/400 responses automatically.

Smart Cache

Smart Glide stores processed images in the configured cache path and tracks metadata in a manifest stored in your cache store.

  • max_size_mb limits total cache footprint.
  • lru_window determines how far back "least recently used" entries remain before eviction.
  • Metadata includes cached_at, last_accessed, and cache_file references to support background cleanup.

Responses include browser cache headers (Cache-Control, Expires) and optional ETag. HTTP clients respecting If-None-Match will receive 304 Not Modified when appropriate.

External Profiles

To allow non-developers to tweak compression parameters, create a PHP file that returns an array:

<?php

return [
    'hero' => ['w' => 1920, 'h' => 1080, 'fm' => 'webp', 'q' => 80],
    'thumbnail' => ['w' => 320, 'h' => 320, 'fit' => 'crop', 'q' => 72],
];

Point SMART_GLIDE_PROFILE_FILE to this file. Its definitions merge on top of the inline profiles config.

CLI Helpers

# Clear all cached renditions
php artisan smart-glide:clear-cache --force

# Pre-warm a single image
php artisan smart-glide:warm products/phone.jpg --profile=hero --widths=640,960,1280

# Pre-warm all source images
php artisan smart-glide:warm --all --ext=jpg,png

# View cache statistics
php artisan smart-glide:stats

# View full manifest
php artisan smart-glide:stats --manifest

Scheduled Cache Purge

The package ships with a built-in Artisan command and scheduler hook:

  • Configure the daily purge time via SMART_GLIDE_CACHE_PURGE_TIME (default 03:00).
  • Smart Glide registers smart-glide:clear-cache automatically; when the scheduler is enabled (php artisan schedule:run), cached renditions are cleared daily at the configured time.
  • Run manually with php artisan smart-glide:clear-cache --force.

Testing

composer test          # PHPUnit (requires Laravel testing harness)
composer test-coverage # Coverage (if configured)
composer analyse       # Static analysis (PHPStan/Psalm if added)

Because this is a Laravel package, use Orchestra Testbench for isolated package tests.

Companion: React / Next.js SDK

For React and Next.js projects, install the companion package smart-glide-react:

npm install smart-glide-react

It provides:

  • <SmartGlideImage> β€” Full-featured <img> component with blur placeholders, priority loading, and JSON-LD
  • <SmartGlidePicture> β€” <picture> element with multiple <source> branches
  • <SmartGlideBackground> β€” Responsive CSS background container
  • useSmartGlide() β€” Hook that fetches image data from the /img-data API
  • smartGlideNextLoader β€” Custom loader for next/image
  • fetchSmartGlideData() β€” Server-side fetcher for Next.js App Router (ISR support)

See the smart-glide-react README for full documentation.

Troubleshooting

Symptom Likely Cause Resolution
403 Smart Glide signature missing/invalid APP_KEY or SMART_GLIDE_SIGNATURE_KEY changed, or URL copied without the signed query string. Restore the original key, run php artisan config:clear & php artisan cache:clear, and always use URLs generated by Smart Glide.
Image returns 404 Asset not found inside config('smart-glide.source'). Move the file into the source directory (default storage/app/public) or update the config accordingly.
Links must be absolute APP_URL missing or incorrect. Set APP_URL in .env; deliveryUrl() honours it when producing URLs.
Cached variant stale Old rendition remains in storage/app/smart-glide-cache. Use SmartGlide::forgetPath('path') or call POST /img-admin/forget to purge.
Admin routes return 404 Admin API not enabled. Set SMART_GLIDE_ADMIN_ENABLED=true in .env and clear config cache.

License

Released under the MIT License.