Serving pages faster with images variations
Magnolia in just 12 minutes
Why not learn more about what Magnolia can do while you have a coffee?
A sensible requirement for any web app is delivering different-size images depending on a visitor’s device. You can save a lot of bandwidth by providing images in a smaller resolution when a mobile device or tablet accesses your site, making it load faster.
This blog article will show you what image resolution options Magnolia brings to the table and how to use them.
Defining image variations
To provide images in different resolutions in Magnolia you first have to define image variations in a theme. The definition is straightforward and consists of
The name of the variation
- for example
- its width for simplicity
The class SimpleResizeVariation
The image’s width
[...]
imaging:
class: info.magnolia.templating.imaging.VariationAwareImagingSupport
variations:
"1600":
class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
width: 1600
"1366":
class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
width: 1366
[...]
If you have not yet assigned a theme to your site, please follow our documentation to do so.
Generating the image URL
In Freemarker
We offer templating functions to provide easy access to digital assets such as images. These functions generate the URL of an image and its requested variation for you. I will show you how to use them later.
In headless mode
Our delivery API supports image variations in headless mode, too. Our asset resolver automatically generates the URL of the image variation. Then it’s the responsibility of the front end to place the image correctly.
An example from our Travel Demo
I would like to share the implementation of image variations in our travel demo to show you how to define variations and use our templating functions efficiently.
Example: Defining the theme
This is how we define variations in a theme.
Excerpt from travel-demo-theme.yaml:
cssFiles:
[...]
jsFiles:
[...]
lazysizes:
addFingerPrint: true
link: /.resources/travel-demo-theme/libs/lazysizes/lazysizes.min.js
[...]
imaging:
class: info.magnolia.templating.imaging.VariationAwareImagingSupport
variations:
"1600":
class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
width: 1600
"1366":
class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
width: 1366
"960":
class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
width: 960
"480":
class: info.magnolia.templating.imaging.variation.SimpleResizeVariation
width: 480
[...]
Besides the image variations themselves, the JavaScript library lazysizes is worth mentioning. It later ensures that Magnolia serves the best image variation depending on a visitor’s screen size. For this to work, we have to generate the correct HTML code of the image, as I will show you next.
Building a Custom Integration for an External DAM in Magnolia
Do you want to easily use images, videos, and other documents across your apps and digital channels? Read our blog to learn how to connect any external Digital Asset Management (DAM) platform to Magnolia.
Example: Generating the image HTML
We use a macro called responsiveImageTravel in our demo. It determines which image variations to consider and which variation to use as a fallback. We will use this macro in our component template.
Excerpt from imageResponsive.ftl:
[...]
[#-- Macro to render a responsive image with the variations configured in the theme. --]
[#macro responsiveImageTravel asset alt="" title="" cssClass="" additional="" constrainAspectRatio=false]
[#if constrainAspectRatio ]
[#assign srcs = [
{"name":"480x360", "width":"480"},
{"name":"960x720", "width":"960"},
{"name":"1366x1024","width":"1366"},
{"name":"1600x1200", "width":"1600"}]]
[#assign fallback="960x720"]
[@responsiveImageLazySizes asset alt title cssClass srcs fallback additional /]
[#else]
[#assign srcs = [
{"name":"480", "width":"480"},
{"name":"960", "width":"960"},
{"name":"1366","width":"1366"},
{"name":"1600","width":"1600"}]]
[#assign fallback="960"]
[@responsiveImageLazySizes asset alt title cssClass srcs fallback additional /]
[/#if]
[/#macro]
[...]
This macro calls a second macro called responsiveImageLazySizes which uses the getRenditions templating function for each variation and the fallback.
This way, we generate the image HTML as described in the example "responsive image with srcset and automatic sizes attribute" in the lazysizes documentation.
Excerpt from imageResponsive.ftl:
[#-- Macro to render responsive image using lazysizes javascript library. Use data-srcset attribute to only load the size of image that the current image width requires --]
[#macro responsiveImageLazySizes asset alt="" title="" cssClass="" srcs="" fallbackName="" additional="" ]
[#if asset?exists]
[#assign cssClass = cssClass + " lazyload"]
[#assign rendition = damfn.getRendition(asset, fallbackName)!]
<noscript>
<img class="${cssClass}" src="${rendition.link}" alt="${alt}" title="${title}" ${additional} />
</noscript>
<img data-sizes="auto" class="${cssClass} lazyload" ${additional} src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="${alt}" title="${title}"
data-srcset="[#compress]
[#list srcs as src]
[#assign rendition = damfn.getRendition(asset, src.name)!]
[#if rendition?exists && rendition?has_content]
${rendition.link} ${src.width}w,
[/#if]
[/#list]
[/#compress]" />
[/#if]
[/#macro]
We can now use the responsiveImageTravel macro anywhere. So, let’s look at an example: the tour teaser macro. This macro uses the previously macro to generate the image HTML.
Excerpt from tourTeaser.ft:
[#macro tourTeaser tour additionalWrapperClass="col-md-6"]
[#include "/travel-demo/templates/macros/imageResponsive.ftl"]
[#include "/tours/templates/macros/tourTypeIcon.ftl" /]
[#assign imageHtml][@responsiveImageTravel tour.image "" "" "tour-card-image" "data-ratio='1.33'" true /][/#assign]
<!-- Tour Teaser -->
<div class="${additionalWrapperClass} tour-card card">
<div class="tour-card-background">
${imageHtml}
</div>
<a class="tour-card-anchor" href="${tour.link!}">
<div class="tour-card-content-shader"></div>
<div class="tour-card-content">
<h3>${tour.name!}</h3>
<div class="category-icons">
[#list tour.tourTypes as tourType]
<div class="category-icon absolute-center-container">
[@tourTypeIcon tourType.icon tourType.name "" /]
</div>
[/#list]
</div>
<div class="card-button">
<div class="btn btn-primary">${i18n['tour.view']}</div>
</div>
</div>
</a>
</div>
[/#macro]
Example: Using the macro in a component
Now, let's see how to use the macros in an actual component.
Their inclusion makes them available in the component template. While iterating through the list of tours, we create a “tourTeaser” component for each tour using the “tourTeaser” macro.
[#-------------- ASSIGNMENTS --------------]
[#include "/tours/templates/macros/tourTeaser.ftl"]
[#include "/travel-demo/templates/macros/editorAlert.ftl" /]
[#if def.parameters.tourType??]
[#assign category = model.getCategoryByName(def.parameters.tourType)]
[#else]
[#assign category = model.getCategoryByUrl()!]
[/#if]
[#assign tours = model.getToursByCategory(category.identifier)]
[#assign title = content.title!i18n.get('tour.all.tours', [category.name!""])!]
[#-------------- RENDERING --------------]
<!-- Tour List -->
<div class="container tour-list">
<h2>${title}</h2>
<div class="row">
[#list tours as tour]
[@tourTeaser tour /]
[/#list]
</div>
[@editorAlert i18n.get('note.for.editors.assign.category', [category.name!""]) /]
</div>
<script>
jQuery(".tour-card-image").objectFitCoverSimple();
</script>
At the end of this component, we run the jQuery function objectFitCoverSimple using the CSS class tour-card-image we passed in the macro tourTeaser.ftl when creating the HTML.
Conclusion
With Magnolia it’s easy to deliver different image resolutions depending on the user’s screen size. This functionality is not limited to Freemarker but works for modern headless implementations, too. This is another example of Magnolia’s flexibility, delivering real-life szenarios.