PHPackages                             schliesser/imaginator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. schliesser/imaginator

ActiveTypo3-cms-extension

schliesser/imaginator
=====================

Imaginator - Zero-config responsive images for TYPO3: signed srcset ladders, local or external processing.

v1.0.0(1mo ago)019[3 issues](https://github.com/schliesser/imaginator/issues)GPL-2.0-or-laterPHPPHP &gt;=8.3CI passing

Since Jul 18Pushed 1mo agoCompare

[ Source](https://github.com/schliesser/imaginator)[ Packagist](https://packagist.org/packages/schliesser/imaginator)[ RSS](/packages/schliesser-imaginator/feed)WikiDiscussions main Synced 2w ago

READMEChangelogDependencies (23)Versions (2)Used By (0)

Imaginator
==========

[](#imaginator)

Zero-config responsive images for TYPO3. The integrator writes **no `sizes` and no per-image breakpoints**: at render time the extension emits a real ``/`` with a quantized width-ladder `srcset` and `sizes="auto"`, so the browser's preload scanner fetches the correctly-sized image in a single request.

**For editors.** A backend **aspect-ratio field** per content element lets editors pick the framing once — **per breakpoint** — and get **uniform, consistent images** across every element of that type, no more ragged grids from mismatched upload dimensions. Crop and focus area are honored, so the chosen subject stays in frame at every size.

[![Aspect-ratio field in the backend](Documentation/Images/AspectRatioField.png)](Documentation/Images/AspectRatioField.png)

**For developers.** Drop one `` and get sharp, perfectly-sized images on every device — no hand-tuned `sizes`, no breakpoint lists, no layout shift (CLS). Stop shipping oversized images: the width ladder bounds processing to a fixed set of sizes, so you serve fewer, smaller bytes and score better Core Web Vitals (LCP/CLS) out of the box.

**Features.** Quantized width-ladder `srcset` + `sizes="auto"` · a single uniform output format (**AVIF** by default, or **WebP**) · per-breakpoint **art direction** (crop variants + focus area honored) · **low-quality placeholders** (ThumbHash, dominant-color, or none) · `priority` images get `fetchpriority="high"`and skip lazy-loading · pluggable processing — classic **sync** on first request, **async** via a signed middleware endpoint (default), or an **external processor** (e.g. imgproxy).

JavaScript is never required for sharpness — it's only a polyfill for Safari &lt; 27.

Requirements
------------

[](#requirements)

- PHP **8.3+** (tested 8.3 / 8.4 / 8.5)
- TYPO3 **13.4 LTS** or **14.3 LTS**
- A working image processor (GraphicsMagick or ImageMagick — TYPO3's standard `GFX` settings) for local processing; alternatively an external processor (e.g. imgproxy)
- A non-empty `encryptionKey` (standard on every TYPO3 install — see *Signing* below)

Installation
------------

[](#installation)

```
composer require schliesser/imaginator
```

A Composer install activates the extension automatically. Installing it is enough to use the `` ViewHelper — no Site Set or TypoScript is required. Configure it under *Settings → Extension Configuration* (see below).

Usage
-----

[](#usage)

Declare the ViewHelper namespace once per template and call ``:

```

    {# Simplest case: one ratio, renders an  with the full ladder #}

    {# By file UID or path #}

    {# Art direction: per-breakpoint ratios render a  with one  each.
       The map is {breakpoint: ratio}; keys are preconfigured breakpoint aliases (see extension
       config) or a px min-width, with 0 / xs as the base. Tiers are mobile-first (min-width
       only); the base (min-width 0) tier becomes the  fallback, larger tiers become
       s. #}

    {# LCP / above-the-fold image: drops lazy-loading, adds fetchpriority="high"
       and an explicit sizes="100vw" #}

```

### Arguments

[](#arguments)

ArgumentTypeDefaultDescription`image``File`/`FileReference`–FAL object (use this *or* `src`)`src`string`''`File **UID** or path (e.g. `fileadmin/img/x.jpg`)`treatIdAsReference`bool`false`Treat `src` as a `sys_file_reference` UID`aspectRatio`string | map`16:9``"W:H"`, or a `{breakpoint: "W:H"}` map for art direction`cropVariant`string`default`FAL crop variant to use`alt`string`''`Alternative text`class`string–CSS class on the ```priority`bool`false`Mark as the LCP image (see below)`width`/`height` are always emitted from the largest rung, so there is zero layout shift.

### Priority / LCP images

[](#priority--lcp-images)

Set `priority="1"` on the above-the-fold (LCP) image. Imaginator then:

- drops `loading="lazy"` and adds `fetchpriority="high"` on the ``;
- renders an explicit `sizes="100vw"` instead of `sizes="auto"`;
- adds a `` to the ``, so the request is discoverable in the initial document before the body is parsed.

Only the most-preferred format is preloaded (gated by `type`, so other browsers don't double-download), and its `imagesrcset`/`imagesizes` mirror the rendered tier exactly so the browser reuses the preload. This satisfies Lighthouse's "LCP request is discoverable", "not lazily loaded" and "fetchpriority should be applied" audits. Use it on **one** image per page.

### `sizes="auto"` and the Safari polyfill

[](#sizesauto-and-the-safari-polyfill)

Zero-config sizing relies on `sizes="auto"`, which lets the browser pick the right `srcset`candidate from the laid-out width — no hand-written `sizes`, no per-image breakpoints. Chrome/Edge (126+) and Firefox (150+) support it natively; **Safari and iOS Safari only from version 27**(WebKit landed it in March 2026; Safari 27 ships in the 2026 cycle). On older Safari/iOS an unsupported `sizes="auto"` falls back to `100vw`, so they fetch an oversized (but still sharp) candidate. (`sizes="auto"` is spec-valid only on `loading="lazy"` images, which is exactly where Imaginator emits it — priority/eager images get an explicit `sizes="100vw"` instead.)

To close that gap, whenever a processed `` renders, Imaginator queues a tiny progressive-enhancement script via the `AssetCollector` (head, `async`, registered once per page): the vendored [Shopify/autosizes](https://github.com/Shopify/autosizes) polyfill (MIT, `Resources/Public/JavaScript/frontend/autosizes.js`). It self-detects native support and backs off; on Safari it measures the rendered width and writes a concrete pixel `sizes`, so the browser picks a right-sized candidate. It is **never load-bearing** — sharpness comes from the server-rendered ladder, so the page works fully with JavaScript disabled — and it only touches ``, so **priority/LCP images are untouched** (they carry an explicit `sizes="100vw"` and `loading="eager"`). Pages without any `` ship no extra JS.

### Crop &amp; focus areas

[](#crop--focus-areas)

When an `image` (a FAL `FileReference`) is rendered, the editor's **crop variant** is resolved server-side: it reads the reference's crop JSON and fits the requested ratio inside the **crop area**, centered on the **focus area** — so the framing the editor chose is honored and **no crop geometry is exposed in the URL**. The ladder is bounded by the crop area, so a tightly-cropped region is never upscaled. A `src` given as a path or plain file uid (`f…`) has no crop data and is center-cropped to the ratio.

Configuration
-------------

[](#configuration)

Configuration is **instance-wide Extension Configuration**. Edit it under *Settings → Extension Configuration → imaginator* in the backend, or set it in `config/system/settings.php`:

```
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['imaginator'] = [
    'lqip' => 'dominant-color',
    'ladder' => '320,640,960,1280,1920',
];
```

SettingDefaultDescription`processor``local:async`Image processor: `local:async` (signed endpoint + 302), `local:sync` (static `srcset`, no middleware) or `imgproxy` (offloaded)`maxDimension``3840`Largest image dimension in px; the ladder is capped to it`ladder``320,420,560,740,980,1300,1720,2000,2560,3200,3840`Width-ladder rungs (comma-separated), covering up to 4K (UHD) displays`format``avif`Single uniform output format: `avif` or `webp``quality.avif``50`AVIF quality (AVIF's scale sits lower than JPEG/WebP for the same perceived quality)`quality.webp``72`WebP quality`lqip``thumbhash`Low-quality placeholder: `thumbhash`, `dominant-color` or `none`The signing secret is derived from the global `encryptionKey`; when it changes, flush the frontend caches so re-rendered HTML carries freshly signed URLs. Settings are global by design, so the render path and the signed-endpoint verify path always agree (a per-site model would risk mismatched ladders → spurious 403s).

### Content Security Policy

[](#content-security-policy)

The placeholder is **not** rendered as an inline `style=""` attribute (those cannot carry a CSP nonce). The `` only gets a CSS class; the actual rule is registered through TYPO3's `AssetCollector` and emitted as a `` element, which TYPO3 nonces automatically when frontend CSP is enabled. Identical placeholders are deduplicated to a single rule.

CSP directives you may need:

- **`thumbhash`** (default): the blurred preview is a `data:` background-image, so allow `img-src data:` (TYPO3's default frontend CSP already does).
- **`dominant-color`**: a plain background color — needs nothing beyond the nonced ``.
- **`none`**: no placeholder, no extra directive.

The local:async image endpoint
------------------------------

[](#the-localasync-image-endpoint)

Each async candidate URL has one of these forms:

```
reference: /_imaginator/{16-hex-signature}/r{referenceUid}/{cropVariant}/{width}x{height}.{ext}
file:      /_imaginator/{16-hex-signature}/f{fileUid}/{cropVariant}/{width}x{height}.{ext}

```

The uid is site-unique, so no storage segment is needed. A PSR-15 middleware verifies the signature, re-checks the width against the ladder, processes the image and **302-redirects to the processed file** with `Cache-Control: public, max-age=31536000, immutable`. A forged or tampered URL returns **403**, and only ladder-quantized widths are ever processed — so the endpoint cannot be abused to exhaust the server with arbitrary sizes.

If the derivative already exists at render time, the static processed-file URL is written straight into the markup — so the web server serves it directly, with no middleware/PHP roundtrip.

The signing secret is derived automatically from `$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']`. No configuration is needed, but note that **changing `encryptionKey` invalidates previously generated URLs** (they will re-sign on the next render).

Contributing
------------

[](#contributing)

A reproducible DDEV demo instance, the full test workflow, and the coding/PR conventions are documented in [CONTRIBUTING.md](CONTRIBUTING.md). In short:

```
ddev start && ddev setup && ddev demo   # demo at https://imaginator.ddev.site/ (admin / Password.1)
ddev test all && ddev lint
```

License
-------

[](#license)

GPL-2.0-or-later.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance91

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

45d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1a00ae72b0b16c1f6cec5516a93cac144b5aec6e3ac824d9cf758636412a6736?d=identicon)[schliesser](/maintainers/schliesser)

---

Top Contributors

[![schliesser](https://avatars.githubusercontent.com/u/11655823?v=4)](https://github.com/schliesser "schliesser (141 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/schliesser-imaginator/health.svg)

```
[![Health](https://phpackages.com/badges/schliesser-imaginator/health.svg)](https://phpackages.com/packages/schliesser-imaginator)
```

###  Alternatives

[friendsoftypo3/content-blocks

TYPO3 CMS Content Blocks - Content Types API | Define reusable components via YAML

103629.7k76](/packages/friendsoftypo3-content-blocks)[typo3/cms-adminpanel

TYPO3 CMS Admin Panel - The Admin Panel displays information about your site in the frontend and contains a range of metrics including debug and caching information.

115.9M74](/packages/typo3-cms-adminpanel)[fluidtypo3/flux

The flux package from FluidTYPO3

1501.0M25](/packages/fluidtypo3-flux)[eliashaeussler/typo3-warming

Warming - Warms up Frontend caches based on an XML sitemap. Cache warmup can be triggered via TYPO3 backend or using a console command. Supports multiple languages and custom crawler implementations.

22283.6k](/packages/eliashaeussler-typo3-warming)[pagemachine/typo3-formlog

Form log for TYPO3

23247.0k8](/packages/pagemachine-typo3-formlog)[typo3/cms-fluid-styled-content

TYPO3 CMS Fluid Styled Content - Fluid templates for TYPO3 content elements.

229.9M469](/packages/typo3-cms-fluid-styled-content)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
