tempest/responsive-image
Generate responsive images in PHP and render the correct HTML. Define your source and public paths, create a factory, then output an img tag with a ready-made srcset of scaled sizes. Early work in progress, so expect changes.
composer require tempest/responsive-image
https://tempestphp.com/docs/packages/responsive-image — the README is intentionally minimal and the docs page is the authoritative source for options not shown in the snippet.ResponsiveImageConfig, pointing srcPath at your source images (usually outside the web root) and publicPath at your actual public/web root:
use Tempest\ResponsiveImage\ResponsiveImageConfig;
use Tempest\ResponsiveImage\ResponsiveImageFactory;
$config = new ResponsiveImageConfig(
srcPath: __DIR__ . '/path/to/image/sources',
publicPath: __DIR__ . '/../public',
);
$factory = new ResponsiveImageFactory($config);
html property:
$image = $factory->create('/parrot.jpg');
echo $image->html;
<img src="..."> for a hero or card image with the generated srcset, letting the browser pick an appropriately sized variant. Start there, then expand to repeated media (CMS uploads, galleries, avatars).Inject the factory, don't construct it inline. The quickstart shows new ResponsiveImageFactory($config) for clarity, but in a real Tempest app register ResponsiveImageConfig in your config and let the container resolve ResponsiveImageFactory in controllers, view models, and template helpers. This keeps the source/public paths in one place.
Render through a small view helper. Rather than calling $factory->create(...) scattered through templates, wrap it (img($path, $attrs)) so you can centrally add sizes, loading="lazy", decoding="async", alt, and class attributes on top of the generated srcset. The package gives you html and the srcset; the surrounding <img> policy is yours.
Pre-generate vs. on-demand. Two viable workflows:
create() at request time for low-traffic or long-tail images; cache-busting matters less if generated files persist in publicPath.tempest command) so production never pays the resize cost and you catch missing/corrupt sources before traffic hits. Good for a fixed set of marketing assets or hero images.Treat srcPath as the single source of truth. Keep originals out of publicPath and only let the package write derived variants there. This keeps your repo/deploy artifact clean and means you can regenerate all derivatives by pointing at the sources directory.
Compose with framework features. Because html and (presumably) the underlying srcset are plain strings, they slot into any view layer — Tempest view components, Blade-style partials, or a JSON API returning {src, srcset} for a JS client to hydrate. For a headless/CMS setup, store the source filename in the DB and generate URLs on render; never store generated variant filenames.
In tests, assert against the returned html string or the parsed srcset attribute (parse it into [url, width] pairs) rather than byte-for-byte comparing a long HTML string — ordering of generated widths should be treated as an implementation detail.
composer.json, and read the changelog between releases. The single star and 0 dependents mean you are effectively an early adopter: API changes and unannounced behavior shifts are realistic. Wrap the factory behind your own thin service so a breaking change or a swap to another package touches one class.srcPath / publicPath must be absolute and correct. Use __DIR__ or framework path helpers. A wrong publicPath produces a valid <img> with a srcset pointing at files that were written somewhere the web server cannot see — the HTML looks fine and the images 404. If images render in the HTML but are broken in the browser, check these two paths first.srcset are derived from the filename, not from publicPath. The example yields /parrot-1920-1280.jpg — i.e. flat filenames at the root of publicPath. If you have a subdirectory structure in srcPath, verify how subpaths map to URLs, and make sure your web server serves that location.publicPath; in Docker/CI, ensure the directory is writable by the PHP user, and that it is not read-only (common when baking the public dir into an image). Failing writes may surface as a missing-file error far from the actual cause.<img> in the README omits sizes, alt, loading, and decoding. Without sizes, the browser assumes 100vw for w-descriptor srcset, which often picks overly large variants on desktop. Always add sizes for anything that isn't full-bleed and alt/loading="lazy" for accessibility and performance.<picture> with multiple <source> elements is the usual shape if the package doesn't emit it.publicPath is the reliable "did I break the cache?" step; in production, tie regeneration to deploy or an explicit content-hash in the source filename.create() on a nonexistent or unreadable source likely throws or emits broken HTML depending on version. Guard content-driven paths (CMS uploads) with an existence check and a graceful placeholder before calling the factory.ResponsiveImageConfig constructor (widths/quality/format knobs as documented) and the ResponsiveImageFactory / resulting image object (methods and properties beyond html). Reading those two classes is the fastest way to discover options the README doesn't advertise.How can I help you explore Laravel packages today?