netresearch / nr-image-optimize
On-demand image optimization for TYPO3 with processed delivery, WebP and AVIF variants, and responsive srcset ViewHelpers.
Package info
github.com/netresearch/t3x-nr-image-optimize
Type:typo3-cms-extension
pkg:composer/netresearch/nr-image-optimize
Requires
- php: >=8.2
- intervention/image: ^3.11 || ^4.0
- typo3/cms-core: ^13.4 || ^14.0
- typo3/cms-extbase: ^13.4 || ^14.0
- typo3/cms-fluid: ^13.4 || ^14.0
Requires (Dev)
Suggests
- christophlehmann/imageoptimizer: Optimize uploaded/processed images with binaries of your choice
- dev-main / 2.3.x-dev
- v2.2.2
- 2.2.1
- 2.2.0
- 2.1.0
- 2.0.1
- 2.0.0
- dev-TYPO3_12 / 1.x-dev
- v1.1.1
- 1.1.0
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.1.5
- dev-fix/acceptance-source-type-attribute
- dev-fix/functional-strict-coverage-xdebug-TYPO3_12
- dev-fix/functional-strict-coverage-xdebug
- dev-fix/70-log-400-responses-functional-test-cache
- dev-fix/70-log-400-responses-functional-test-cache-TYPO3_12
- dev-chore/adopt-release-orchestrator
- dev-renovate/step-security-harden-runner-2.x
- dev-staging
This package is auto-updated.
Last update: 2026-04-24 11:50:44 UTC
README
Image Optimization for TYPO3
The nr_image_optimize extension provides image optimization
for TYPO3 on three layers:
- On upload. Lossless optimization runs automatically when
images are added to, or replaced in, a FAL storage (via
optipng,gifsicle,jpegoptim). - On demand in the frontend. Variants are processed lazily
when first requested through the
/processed/URL, with support for modern formats (WebP, AVIF), responsivesrcsetgeneration, and automatic format negotiation. - In bulk from the CLI. Two console commands iterate the FAL index to optimize or report optimization potential on the entire installation.
Features
- Automatic optimization on upload. PSR-14 event listener compresses newly added or replaced images in place without re-encoding. Storages, offline drivers, and unsupported extensions are handled transparently.
- Bulk CLI commands.
nr:image:optimizewalks the FAL index and compresses eligible images.nr:image:analyzereports optimization potential without modifying files (heuristic, no binaries invoked). - Lazy frontend processing. Variants are created only when
a visitor first requests them through the
/processed/URL. - Modern format support. Automatic WebP and AVIF conversion with fallback to original formats.
- Responsive images. Built-in
SourceSetViewHelperforsrcsetandsizesgeneration. - Render modes. Choose between
coverandfitresize strategies. - Width-based srcset. Optional responsive
srcsetwith configurable width variants andsizesattribute. - Fetch priority. Native
fetchpriorityattribute support for Core Web Vitals optimization. - Middleware-based processing. Lightweight frontend
middleware intercepts
/processed/requests. - Backend maintenance module. View statistics, check system requirements, and clear processed images.
Requirements
- PHP 8.2, 8.3, 8.4, or 8.5.
- TYPO3 13.4 or 14.
- Intervention Image library (installed via Composer automatically).
Optional (for on-upload and CLI optimization)
Install one or more of the following binaries and make them
available in $PATH to enable lossless compression:
optipng-- for PNG files.gifsicle-- for GIF files.jpegoptim-- for JPEG files.
Paths can be overridden per binary via the OPTIPNG_BIN,
GIFSICLE_BIN, and JPEGOPTIM_BIN environment variables.
A set-but-invalid override is treated as authoritative (tool
reported unavailable); there is no silent fallback to $PATH.
Installation
Via Composer (recommended)
composer require netresearch/nr-image-optimize
Manual installation
- Download the extension from the TYPO3 Extension Repository.
- Upload to
typo3conf/ext/. - Activate the extension in the Extension Manager.
Automatic optimization on upload
After installation the PSR-14 event listener is active out of the box. Whenever an image is added or replaced on an online storage (for example via the backend file module or the REST API) the extension:
- Normalizes the file extension (case-insensitive).
- Checks whether an optimizer binary for that extension is installed.
- Runs the tool in place via the
ImageOptimizerservice. - Restores the storage's permission-evaluation state and clears its re-entrancy guard before returning.
No configuration is required. If no optimizer binary is available the listener silently skips the file.
CLI commands
Bulk optimize existing images
# Dry-run: report what would be processed. vendor/bin/typo3 nr:image:optimize --dry-run # Optimize all eligible images in storage 1 with lossy JPEG # quality 85 and EXIF stripping. vendor/bin/typo3 nr:image:optimize \ --storages=1 \ --jpeg-quality=85 \ --strip-metadata
Supported options:
--dry-run- Analyze only, do not modify files.
--storages- Restrict to specific storage UIDs (comma-separated or repeated).
--jpeg-quality- Lossy JPEG quality 0--100. Omit for lossless optimization.
--strip-metadata- Remove EXIF and comments when the tool supports it.
Analyze optimization potential
vendor/bin/typo3 nr:image:analyze \
--storages=1 \
--max-width=2560 \
--max-height=1440 \
--min-size=512000
The analyzer never invokes an external tool. It estimates
savings from file size and resolution and reports how much
disk space could be recovered by running
nr:image:optimize.
Supported options:
--storages- Restrict to specific storage UIDs.
--max-width/--max-height- Target display box. Images larger than this box are
assumed to be downscaled by
nr:image:optimizeand the estimate factors in the area reduction. --min-size- Skip files smaller than this many bytes (default 512000). Prevents reporting on already-tiny images.
Configuration
The extension works out of the box with sensible defaults.
Images accessed via the /processed/ path are automatically
optimized by the frontend middleware; uploaded images are
compressed by the event listener.
ViewHelper usage
{namespace nr=Netresearch\NrImageOptimize\ViewHelpers}
<nr:sourceSet path="{f:uri.image(image: image)}"
width="1200"
height="800"
alt="{image.properties.alternative}"
sizes="(max-width: 768px) 100vw, 50vw"
responsiveSrcset="1"
/>
Supported parameters
path(required)- Public path to the source image (e.g.
/fileadmin/foo.jpg). Typically generated viaf:uri.image(). width/height- Base width/height in px for the rendered
<img>. Defaults to0(auto from file / preserve aspect ratio). set- Responsive set in the form
{maxWidth: {width: int, height: int}}— emits per-breakpoint<source>tags wrapped in a<picture>. alt/title- HTML-escaped
altandtitleattributes. class- CSS classes for the
<img>tag. Includelazyloadto switch to JS-based lazy loading (data-src/data-srcset). mode- Render mode:
cover(default, crop/fill) orfit(scale inside the box). lazyload- Add
loading="lazy"(native lazy loading). responsiveSrcset- Enable width-based responsive
srcsetinstead of density-based 2x. Default:false. widthVariants- Width variants for responsive
srcset(comma-separated string or array). Default:480, 576, 640, 768, 992, 1200, 1800. sizes- Responsive
sizesattribute. Default:auto, (min-width: 992px) 991px, 100vw. fetchpriority- Native HTML
fetchpriorityattribute (high,low, orauto). attributes- Extra HTML attributes merged into the rendered tag.
Note
Quality and output format are not exposed as ViewHelper
arguments; they are baked into the /processed/ URL
(q<n> segment, source extension). Use
f:uri.image(image: image, additionalConfiguration: ...)
to influence the generated path if needed.
Variant URL format
Processed variants are served from
/processed/<path>.<mode-config>.<ext>. The mode config is a
concatenation of any of w<n>, h<n>, q<n>, m<n>
(width, height, quality 1--100, mode 0 = cover / 1 = fit).
The processor decides at response time whether to serve the
original, the .webp sidecar, or the .avif sidecar based on
the Accept header and the skipWebP / skipAvif query
flags. Path traversal sequences are rejected; w / h are
clamped to 1--8192 and q to 1--100.
Example: /processed/fileadmin/hero.w1200h800m0q85.jpg.
Extension points
Two PSR-14 events let integrators observe the on-demand pipeline:
ImageProcessedEvent- Dispatched after a new variant has been written to disk. Exposes source path, variant path, extension, dimensions, quality, mode, and whether WebP / AVIF sidecars were produced.
VariantServedEvent- Dispatched immediately before the response body is
streamed. Reports whether the response was served from
disk cache (
fromCache).
Image driver selection is handled by ImageManagerFactory:
Imagick is preferred when the PHP extension is loaded, GD is
used as a fallback. The version-agnostic ImageReaderInterface
hides the Intervention Image v3/v4 API difference so integrators
can rely on a stable contract.
Documentation
Full documentation is available in the Documentation/
directory and is rendered on
docs.typo3.org.
Development and testing
# Run all tests composer ci:test # Run specific tests composer ci:test:php:cgl # Code style composer ci:test:php:lint # PHP syntax composer ci:test:php:phpstan # Static analysis composer ci:test:php:unit # PHPUnit tests composer ci:test:php:rector # Code quality # Dockerized test runner (also used by CI) Build/Scripts/runTests.sh -s unit -p 8.4
License
GPL-3.0-or-later. See the LICENSE file for details.
Support
For issues and feature requests, please use the GitHub issue tracker.
Credits
Developed by Netresearch DTT GmbH.