Standard Image Rendering
Images in TYPO3 v10 are typically rendered using a Fluid ViewHelper. The media ViewHelper is used for this purpose; it renders the appropriate HTML markup depending on the resource (image or video). The Fluid markup relevant to us is located in the file typo3/sysext/fluid_styled_content/Resources/Private/Partials/Media/Rendering/Image.html:
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:media
class="image-embed-item"
file="{file}"
width="{dimensions.width}"
height="{dimensions.height}"
alt="{file.alternative}"
title="{file.title}"
loading="{settings.media.lazyLoading}"
/>
</html>
The entire markup is very lightweight—which was a pleasant surprise to me. The documentation for the media-ViewHelper https://docs.typo3.org/other/typo3/view-helper-reference/master/en-us/typo3/fluid/latest/Media.html lists additional arguments beyond those shown here, such as the new “loading” argument.
The `loading` argument is available for the media-ViewHelper starting with v10.3 and uses the value `lazyH`by default. The `loading` argument can be customized (lazy, eager, or auto) using the constant `styles.content.image.lazyLoading `, which was introduced by `fluid_styled_content`. Here is an explanation of the ` loading` attribute: https://web.dev/native-lazy-loading/#the-loading-attribute
And here's what the HTML output looks like when you use the code shown above:
<div id="c1" class="frame frame-default frame-type-image frame-layout-0">
<div class="ce-image ce-center ce-above">
<div class="ce-gallery" data-ce-columns="1" data-ce-images="1">
<div class="ce-outer">
<div class="ce-inner">
<div class="ce-row">
<div class="ce-column">
<figure class="image">
<img class="image-embed-item" title="Demo Bild Title Text" alt="Demo Bild Alt Text" src="/fileadmin/_processed_/6/e/csm_image_01_a55d08e819.jpg" loading="lazy" width="600" height="337">
</figure>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
As you can see, the previously mentioned ` loading` attribute was also rendered with the value ` lazy`. The HTML structure surrounding the ` img ` tag comes from other Fluid templates, most of which are located here: `typo3/sysext/fluid_styled_content/Resources/Private/Partials/Media/`. Let’s take a closer look at the `media` ViewHelper.
Customize media-ViewHelper
The media-ViewHelper lacks arguments such as srcset or sizes. However, the srcset attribute is necessary so that we can override an image’s src attribute with a set of image sources.
To integrate both arguments, we need our own Fluid partial that overrides the original (...Partial/Media/Rendering/Image.html). To do this, I created my own small extension (responsive_images_demo) via https://www.sitepackagebuilder.com/ —based on TYPO3 v10 and Fluid Styled Content.
You can then use the following constants to specify where, for example, your own partials or templates are located:
styles {
templates {
layoutRootPath = EXT:responsive_images_demo/Resources/Private/Layouts/ContentElements/
partialRootPath = EXT:responsive_images_demo/Resources/Private/Partials/ContentElements/
templateRootPath = EXT:responsive_images_demo/Resources/Private/Templates/ContentElements/
}
}
My own image partial is located at typo3conf/ext/responsive_images_demo/Resources/Private/Partials/ContentElements/Media/Rendering/Image.html and automatically overrides the original (typo3/.../Partial/Media/Rendering/Image.html).
Integrate srcset / sizes
The media ViewHelper is extended to include the srcset and sizes attributes using the uri.image ViewHelper and the additionalAttributes argument:
<f:media
class="image-embed-item"
file="{file}"
width="{dimensions.width}"
height="{dimensions.height}"
alt="{file.alternative}"
title="{file.title}"
loading="{settings.media.lazyLoading}"
additionalAttributes="{srcset: '{f:uri.image(image: file, maxWidth: 768)} 768w,
{f:uri.image(image: file, maxWidth: 990)} 990w,
{f:uri.image(image: file, maxWidth: 1200)} 1200w,
{f:uri.image(image: file, maxWidth: 1440)} 1440w,
{f:uri.image(image: file, maxWidth: 1900)} 1900w',
sizes: '(min-width: 1200px) 50vw, 100vw'}"
/>
The srcset and sizes values shown here are just examples. It’s up to you, of course, to decide for which device characteristics you want to provide the corresponding resolutions.
And here’s what the HTML output looks like on the front end:
<img
srcset="/fileadmin/_processed_/6/e/csm_image_01_8159d1e3b0.jpg 768w,
/fileadmin/_processed_/6/e/csm_image_01_19d1039365.jpg 990w,
/fileadmin/_processed_/6/e/csm_image_01_9cee8957cf.jpg 1200w,
/fileadmin/_processed_/6/e/csm_image_01_922756210c.jpg 1440w,
/fileadmin/_processed_/6/e/csm_image_01_0e137c815f.jpg 1900w"
sizes="(min-width: 1200px) 50vw, 100vw"
class="image-embed-item"
title="Demo Bild Title Text"
alt="Demo Bild Alt Text"
src="/fileadmin/_processed_/6/e/csm_image_01_a55d08e819.jpg"
loading="lazy"
width="600"
height="337"
>
The width and height attributes can be adjusted using the following constants:
- styles.content.textmedia.maxW
- styles.content.textmedia.maxWInText - Same as maxW, but this is the maximum width when text wraps around a block of media elements. The default value is 50% of the normal maximum media element width (→ maxW)
The default values in TYPO3 v10.4 are 600 for maxW and 300 for maxWInText.
Alternative Fluid Option
img tag
An alternative way to render responsive images—instead of using f:media or f:image —is to use a standard img tag in combination with the f:uri.image ViewHelper:
<img
class="image-embed-item lazy"
data-src="{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1168)}"
data-srcset="{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 768)} 768w,
{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 990)} 990w,
{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1200)} 1200w,
{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1440)} 1440w,
{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1900)} 1900w"
data-sizes="(min-width: 1200px) 1168px, 100vw"
alt="{file.alternative}"
title="{file.title}"
width="{dimensions.width}"
height="{dimensions.height}"
/>
In the example, the src, srcset, and sizes attributes are given the "data-" prefix, which is often required for lazy-loading scripts, such as https://github.com/verlok/lazyload.
picture tag
The `picture ` tag can be configured in another way—just like the ` img ` tag example shown here—using the ` f:uri.image ` ViewHelper:
<picture>
<source
media="(min-width: 990px)"
srcset="{f:uri.image(image: file, maxWidth: 1200)}"
/>
<source srcset="{f:uri.image(image: file, maxWidth: 768)}" />
<img src="{f:uri.image(image: file, maxWidth: 768)}" alt="{file.alternative}" />
</picture>
TypoScript variant
In the past, responsive images were very often rendered using TypoScript, but later, as Fluid grew in popularity, this approach was increasingly phased out. Nevertheless, rendering responsive images using TypoScript still has its place, especially if the site is (still) largely rendered using TypoScript. TypoScript is a powerful tool and offers great flexibility when it comes to rendering. Sometimes, elements can be implemented more easily with just a few lines of TypoScript than with nested Fluid constructs. Just a glance at the documentation for the TypoScript IMAGE object reveals its potential: https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/ContentObjects/Image/Index.html
The following TypoScript examples focus only on the areas relevant to responsive images. The IMAGE documentation mentioned above includes complete examples, as well as their HTML output.
The example shows a typical IMAGE object that has been extended with various properties so that it includes, for example, the srcset attribute(###SRCSETCANDIDATE###):
10 = IMAGE
10 {
file = fileadmin/example.jpg
file.width = 3141
layoutKey = default
layout {
[...]
srcset {
element = <img src="###SRC###" srcset="###SOURCECOLLECTION###" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>
source = |*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###
}
[...]
}
{
small {
width = 800
srcsetCandidate = 800w
mediaQuery = (min-device-width: 800px)
dataKey = small
}
[...]
}
}
20 < 10
20.layoutKey = srcset
[...]
The IMAGE object can, for example, be defined as a reusable TypoScript object named lib.responsiveImage:
lib.responsiveImage {
default = IMAGE
default {
file {
import.current = 1
treatIdAsReference = 1
}
altText.field = alternative
titleText.field = title
params = class="image-embed-item"
layoutKey = srcset
layout {
[...]
srcset {
element = <img src="###SRC###" srcset="###SOURCECOLLECTION###" sizes="100vw" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>
source = |*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###
}
}
sourceCollection {
mobile {
maxW = 314
srcsetCandidate = 314w
dataKey = mobile
}
[...]
}
}
}
[...]
renderObj < lib.responsiveImage.default
renderObj {
[...]
sourceCollection {
tablet {
width = 768
srcsetCandidate = 768w
mediaQuery = (max-device-width: 768px)
dataKey = tablet
}
[...]
}
}
Or in a Fluid template:
<f:cObject
typoscriptObjectPath="lib.responsiveImage.default"
data="{file.uid}"
/>
Cropping
The Crop feature has been available in several versions of TYPO3 (since v7.6), providing editors with a tool to define a specific section of an image.
The next two code snippets are intended to demonstrate one possible way to customize the crop options available in the Backend for image manipulation, as well as how to access the cropped sections.
TCEFORM
The "default" crop variant, which comes standard with TYPO3, as well as additional variants, can be customized using TCEFORM:
TCEFORM {
sys_file_reference.crop.config.cropVariants {
default {
title = Alle (Standard)
selectedRatio = NaN
allowedAspectRatios {
NaN {
title = Frei
value = 0.0
}
[...]
4:3 {
title = 4:3
value = 1.333333
}
}
}
card {
title = Card
selectedRatio = 16:9
allowedAspectRatios {
16:9 {
title = 16:9
value = 1.777777778
}
}
}
}
}
The ` allowedAspectRatios ` property can be used to specify different aspect ratios. This provides the editor with a selection of predefined aspect ratios for an image, allowing them, for example, to quickly select the appropriate aspect ratio for specific layout requirements.
In the example above, the new “card” variant was added in addition to the “default” variant, which is overridden here. If only one new variant is defined—such as the “card” variant in the example—then no “default” variant is made available to the user in the “ Backend.”
Fluid
In the fluid template, the corresponding variant is accessed using the `cropVariant ` argument of the ` f:uri.image ` ViewHelper. In this example, it is `card`:
<img
class="lazy"
data-src="{f:uri.image(src: file, treatIdAsReference: 1, maxWidth: 768, cropVariant: 'card')}"
data-srcset="{f:uri.image(src: file, treatIdAsReference: 1, maxWidth: 768, cropVariant: 'card')} 768w,
{f:uri.image(src: file, treatIdAsReference: 1, maxWidth: 1900, cropVariant: 'card')} 1200"
data-sizes="..."
alt="..."
[...]
/>
As a supplement, I’d also like to mention the approach using TCA: https://docs.typo3.org/m/typo3/reference-tca/10.4/en-us/ColumnsConfig/Type/ImageManipulation.html
It’s worth taking a look at the TCA documentation, as it explains, among other things, how to use the `coverArea` property to, for example, highlight image areas for the editor that may contain a text area in the front end that will later cover the image, or the `focusArea` property , which stores coordinates (focal point) that can be used, for example, by JavaScript plugins such as https://github.com/jonom/jquery-focuspoint.
Extensions
If you want to save yourself the trouble of writing code to integrate responsive images into Fluid or TypoScript, or if you need more functionality than the Fluid ViewHelpers like f:media or f:image offer, you can always look into TYPO3 extensions.
In the TYPO3 community, the following two TYPO3 extensions are frequently used for rendering responsive images:
vhs media ViewHelper
The vhs TYPO3 extension offers a wide range of useful ViewHelpers, including the v:media.image ViewHelper, which is similar to the well-known ViewHelpers f:media and f:image. Even a quick glance at the vhs ViewHelper reveals how easily, for example, the various srcset values can be defined:
<v:media.image
src="path/to/media.jpg"
width="123"
height="123"
[...]
treatIdAsReference="1"
srcset="314, 768, 990, 1200"
srcsetDefault="768"
/>
The v:media.image ViewHelper reference lists a few more interesting arguments: https://docs.typo3.org/p/fluidtypo3/vhs/main/en-us/ViewHelpers/Media/Image.html
sms_responsive_images
In addition to vhs, the TYPO3 extension sms_responsive_images is another recommendation. Here, too, the Fluid configuration is relatively simple and offers quite a few features:
<sms:image image="{image}" srcset="400, 600, 800, 1000" />
<sms:image image="{image}" srcset="1x, 2x" />
<sms:image
image="{image}"
sizes="(min-width: 1200px) 600px, (min-width: 900px) 800px, 100vw"
/>
<sms:image
image="{image}"
breakpoints="{
0: {'cropVariant': 'desktop', 'media': '(min-width: 1000px)', 'srcset': '1000, 1200, 1400, 1600'},
1: {'cropVariant': 'mobile', 'srcset': '400, 600, 800, 1000, 1200, 1400, 1600'}
}"
/>
<sms:image image="{image}" srcset="400, 600" lazyload="true" />
The extension renders a <img> or <picture> tag, depending on which arguments are used. For example, a picture tag is rendered when the " breakpoints " argument is used.
In addition to the examples shown here, you’ll find more in the extension documentation: https://extensions.typo3.org/extension/sms_responsive_images
Bonus
fileExtension / WebP
Starting with TYPO3 v10.3, it is now also possible to specify the option "webp" for the " fileExtension " argument in the <f:image>, <f:media>, and <f:uri.image> ViewHelpers:
<picture>
<source srcset="{f:uri.image(image: file, treatIdAsReference: true, fileExtension: 'webp')}" type="image/webp"/>
<source srcset="{f:uri.image(image: file, treatIdAsReference: true, fileExtension: 'jpg')}" type="image/jpeg"/>
<f:image alt="{file.alternative}" image="{file}" treatIdAsReference="true"/>
</picture>
Before proceeding, you should verify that the version of ImageMagick you are using supports WebP conversion.
Additional links on using WebP with TYPO3: