Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Thumbhash Laravel Laravel Package

iamriajul/thumbhash-laravel

Laravel wrapper for ThumbHash, the compact image placeholder format. Generate tiny hashes from images and render blurred previews while full-size images load, helping reduce layout shift and improve perceived performance.

View on GitHub
Deep Wiki
Context7

Getting Started

iamriajul/thumbhash-laravel wraps the ThumbHash algorithm — a tiny (typically ~25–35 byte) binary placeholder for an image — in a Laravel-friendly facade. You give it an image, you get a short string you can store in your database; later you turn that string back into a data URI you can drop straight into an <img src> or CSS background-image for blur-up loading.

Install and first encode/decode:

composer require iamriajul/thumbhash-laravel
use IamRiajul\ThumbHash\Facades\ThumbHash;

$hash = ThumbHash::encode(public_path('uploads/photo.jpg')); // short base64-ish string
$dataUri = ThumbHash::decode($hash);                         // "data:image/png;base64,..."

Where to look first:

  1. The package's service provider / facade to confirm the exact facade name and method signatures in your installed version — the API surface is small (encode, decode) but names have shifted between early releases.
  2. The README's "Requirements" section — under the hood it uses intervention/image, so the Intervention major version (v2 vs v3) you already have installed determines which backend (GD vs Imagick) is used.
  3. config/thumbhash.php if published, for driver/size options.

First real use case: add a thumbhash column to your images/posts table, generate the hash once at upload time, and render it as the placeholder while the full asset downloads. That single change is where 90% of the value is.

Schema::table('posts', function (Blueprint $table) {
    $table->string('thumbhash', 100)->nullable()->after('image_path');
});

Implementation Patterns

Generate at upload, store the hash — never on read. Encoding decodes the source file and resizes it, which is orders of magnitude more expensive than a decode. Do it in the same place you already process the upload (a UploadedImage action, a PhotoObserver, or a queued GenerateThumbHash job):

$post->update([
    'image_path' => $path,
    'thumbhash'  => ThumbHash::encode(Storage::disk('public')->path($path)),
]);

Note the ->path() call: the encoder wants a filesystem path, not a Storage disk-relative key.

Blur-up placeholder in Blade. The decoded data URI is a small PNG (ThumbHash caps out around 32×32), so render it as the src and let CSS scale and blur it:

<div class="relative">
    <img src="{{ ThumbHash::decode($post->thumbhash) }}"
         class="absolute inset-0 w-full h-full object-cover blur-lg scale-110"
         aria-hidden="true">
    <img src="{{ Storage::url($post->image_path) }}"
         class="relative w-full h-full object-cover"
         loading="lazy" alt="{{ $post->title }}">
</div>

Average-colour fallback. For cards where you want a flat tint rather than a blurred image, keep a second column with the dominant colour and use the hash only on detail pages — the data URI is ~1–2 KB of HTML per image, which adds up on a 60-card grid.

API / Inertia responses. Decode server-side and ship the data URI as a normal string prop; there's no JS counterpart required. If you'd rather decode client-side to keep payloads small, store the raw hash and decode in the browser with the thumbhash npm package — the formats are interoperable.

Backfilling existing rows. Write an artisan command that chunks the table, skips rows with a non-null thumbhash, and dispatches a job per chunk. Guard it with ->whereNotNull('image_path')->whereNull('thumbhash') so re-runs are idempotent.

Testing. Encoding is deterministic for a given input image, so assert on the decoded output length or non-emptiness rather than the exact string, and commit one small fixture image to tests/Fixtures.

Gotchas and Tips

Upgrade to 1.0.3 if you're on PHP 8.1+. This release specifically fixes a float-conversion deprecation warning raised inside Intervention Image ("Implicit conversion from float … to int loses precision"). It's cosmetic — thumbnails still generate — but if you run with error_reporting(E_ALL) in tests or a strict deprecation-to-exception handler, it will fail your suite. Pin ^1.0.3.

Driver mismatch is the most common failure. The package inherits whatever intervention/image version your app already requires. If another dependency locks you to Intervention v2, the v3-only code paths won't behave as documented. Check composer show intervention/image before debugging anything else, and make sure the GD or Imagick PHP extension is actually installed — a missing extension surfaces as a confusing "Unable to read image" rather than an extension error.

Paths, not URLs. encode() needs a real filesystem path. Passing asset(...), Storage::url(...), or a disk-relative key will fail or, worse, silently encode nothing. Prefer Storage::disk($disk)->path($key) and handle the case where the disk is remote (S3) — you'll need to pull the file to a temp path first, which is another reason to generate hashes at upload time rather than lazily.

Non-image and corrupt files. encode() throws on unreadable input. Wrap backfill commands and user-upload hooks in try/catch and leave thumbhash null on failure; the front end should treat null as "render a solid colour" rather than blowing up a listing page.

Column sizing. A thumbnail hash is small, but base64 padding and future format tweaks mean you shouldn't use string(32). string(100) or text costs nothing and avoids a truncation bug that's only visible on some images.

Decode cost in loops. Decoding is fast in absolute terms but it's still PNG encoding per call. For pages rendering more than ~50 images, either cache the resulting data URI (Cache::remember("thumbhash:{$post->id}", 3600, fn () => ThumbHash::decode($post->thumbhash))) or store the decoded data URI alongside the hash at generation time.

Blur is on you. ThumbHash gives you a low-resolution image, not a blur. Without filter: blur() plus a slight scale() to hide the soft edges, upscaled placeholders look blocky and pixelated rather than like native blur-up.

Extension points are thin. There's no pluggable encoder strategy — if you need a different placeholder format (BlurHash, LQIP), keep it behind your own PlaceholderGenerator interface so you can swap the package out without touching views. The value of this package is the one-line Laravel binding, not deep configurability; treat it as a thin adapter and don't build architecture on top of it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core