Native Lazy Loading for Images

The new standard for image loading in browsers

Since Chrome 76, native image lazy loading can be used - other browsers followed since. 

<img src="..." alt="..." loading="lazy|eager|auto">

Implementing this in Neos is rather easy, as you can work with Fusion prototypes.

The following code will make all your images lazyload (which is probably not what you want, but helps as an example):

prototype(Neos.Neos:ImageTag) {
    attributes.loading = 'lazy'

It might be a better option to eagerly load certain images (those above the fold), but lazy-load most of them. This can be accomplished by several means.

Decide for the editor

You as an integrator could decide which areas of images to eager-load. If you have a hero-stage with a fixed image, this would be a good shot for eager-loading

prototype(MyAwesome.Package:HeroStage) < prototype(Neos.Fusion:Component) {
	image = Neos.Neos:ImageTag {
        attributes.loading = 'eager'

Let the editor decide

You could simply provide different Image-NodeTypes for eager and lazy loading:

prototype(MyAwesome.Package:LazyImage) < prototype(Neos.Neos:ImageTag) {
    attributes.loading = 'lazy'
prototype(MyAwesome.Package:EagerImage) < prototype(Neos.Neos:ImageTag) {
    attributes.loading = 'eager'

You could also provide the editor with a property for handling the loading and adding a nice default:

  abstract: true
      type: string
      defaultValue: ''
        label: 'Loading'
        reloadIfChanged: true
          group: 'image'
          position: 500
          editor: 'Neos.Neos/Inspector/Editors/SelectBoxEditor'
                label: 'default'
                label: 'lazy'
                label: 'eager'

    'Neos.NodeTypes.BaseMixins:ImageMixin': true
    'Neos.NodeTypes.BaseMixins:LinkMixin': true
    'Neos.NodeTypes.BaseMixins:ImageCaptionMixin': true
    'Neos.NodeTypes.BaseMixins:ImageAlignmentMixin': true
    'MyAwesome.Package:ImageLoadingMixin': true
prototype(Neos.Neos:ImageTag) {
    attributes.loading = Neos.Fusion:Case {
        loadingProperty {
            condition = ${q(node).property('loading')}
            renderer = ${q(node).property('loading')}
            @position = 'start'

        default {
            condition = true
            @position = 'end'
            renderer = ${'auto'}