Standard image rendering
Images in TYPO3 v10 are usually rendered via a Fluid-ViewHelper. The media-ViewHelper is used here, which renders the corresponding HTML markup depending on the resource (image or video). The fluid markup relevant for us can be found 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 surprised me in a positive way. In addition to the arguments shown here, the documentation for the media-ViewHelper https://docs.typo3.org/other/typo3/view-helper-reference/master/en-us/typo3/fluid/latest/Media.html shows others, such as the new loading argument.
The loading argument is available for the media-ViewHelper from v10.3 and is used with the value lazyHby default. The loading argument can be customized(lazy, eager or auto) via the constant introduced for this purpose by fluid_styled_content styles.content.image.lazyLoading. There is an explanation of the loading attribute here: https://web.dev/native-lazy-loading/#the-loading-attribute
And this is what the HTML output looks like when using 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 aforementioned loading attribute was also rendered with the value lazy. The HTML construct around the img tag comes from other Fluid templates, most of which can be found 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 overwrite the src attribute of an image with a set of image sources.
In order to be able to integrate both arguments, we need our own Fluid partial that overwrites the original (...Partial/Media/Rendering/Image.html). For this I have 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 your own partials or templates are located, for example:
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 under typo3conf/ext/responsive_images_demo/Resources/Private/Partials/ContentElements/Media/Rendering/Image.html and automatically overwrites the original(typo3/.../Partial/Media/Rendering/Image.html).
srcset / integrate sizes
The media ViewHelper is supplemented with the attributes srcset and sizes using the ViewHelper uri.image and the argument additionalAttributes:
<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 shown here are examples. It is of course up to you to decide which device characteristics you want to provide the corresponding resolutions for.
And this is what the HTML output in the frontend looks like:
<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 - Like maxW but this is the maximum width when text is wrapped around a block of media elements. Default value is 50% of the normal maximum media element width → maxW)
The default value in TYPO3 v10.4 is for maxW → 600 and for maxWInText → 300.
Alternative fluid variant
img tag
An alternative way to render responsive images instead of using f:media or f:image is to use a normal 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 attributes src, srcset and sizes 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 equipped with the f:uri.image ViewHelper as a further variant, like the img tag example shown:
<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 the increasing popularity of Fluid pushed this more and more out of the way. Nevertheless, rendering responsive images using TypoScript still has its raison d'être, especially if the site is (still) largely rendered using TypoScript. TypoScript is a powerful tool and offers great flexibility in rendering. Sometimes it is easier to implement elements with just a few lines of TypoScript than with nested Fluid constructs. A look at the documentation for the TypoScript IMAGE object alone shows the potential: https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/ContentObjects/Image/Index.html
The following TypoScript examples only show the areas that are relevant for Responsive Images. The IMAGE documentation mentioned contains 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 receives the srcset attribute(###SRCSETCANDIDATE###), for example:
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 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
Since some TYPO3 versions (v7.6) the crop feature exists, which gives editors a tool to define a section of an image.
The next two code sections are intended to show a possible way to customize the crop variants that are available in the backend for image manipulation and how to access the sections.
TCEFORM
The crop variant default, which TYPO3 already provides by default, as well as additional variants can be customized via 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 gives the editor different predefined aspect ratios for an image, e.g. to select the right aspect ratio for certain layout specifications more quickly.
In the example above, the new card variant has been added in addition to the default variant, which is overwritten 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 via the cropVariant argument of the f:uri.image ViewHelper. In this example, this 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 would like to mention the way via TCA: https://docs.typo3.org/m/typo3/reference-tca/10.4/en-us/ColumnsConfig/Type/ImageManipulation.html
It is worth taking a look at the TCA documentation because, among other things, the use of the coverArea is also shown, e.g. to display image areas for the editor, which can contain a text area in the frontend that later covers the image or the focusArea property, which stores coordinates (focal point) that can be used for JavaScript plugins such as https://github.com/jonom/jquery-focuspoint, for example.
Extensions
If you want to save yourself some typing work when integrating the Responsive Images into Fluid or TypoScript or perhaps need more functionality than the Fluid ViewHelpers such as f:media or f:image offer, you can always look around for TYPO3 extensions.
In the TYPO3 community, the following two TYPO3 extensions are often used for rendering Responsive Images:
vhs media ViewHelper
The vhs TYPO3 extension offers a variety of useful ViewHelpers, including the v:media.image ViewHelper, which is similar to the well-known ViewHelpers f:media or f:image. A first look at the vhs-ViewHelper already shows the simplicity with which, for example, the different srcset specifications 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 some other 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 has a number of 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 an <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 can find more in the extension documentation: https://extensions.typo3.org/extension/sms_responsive_images
Bonus
fileExtension / WebP
As of TYPO3 v10.3 it is now also possible to specify the option webp for the argument fileExtension in the ViewHelpers <f:image>, <f:media> and <f:uri.image>:
<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>
You should first check whether the ImageMagick version you are using supports WebP conversion.
Further links on the use of WebP in conjunction with TYPO3: