One of the problems that can arise while solving layout shifts is dynamic blocks such as cms blocks. Before content is loaded on the page, it is unknown what will be dimensions of the CMS block, because it depends on the content length inside of it.

To resolve the corresponding layout shift issue, the following actions are required:

1. How to add rendering of placeholder for dynamic block

<aside> 💡 Placeholder can be provided for any dynamic block, in this guide for example puprose was taken cms blocks.

</aside>

Lets consider that category page renders cms block in CategoryDetails component. While this block is loading, there is no placeholder, when content is fetched from server, cms block renders on page and shifts ProductList component down.

Peek 2023-02-05 23-23.gif

Unfortunately, it is not possible to predict what will be length of content, hence its dimensions will be unknown before it will be rendered. But to minimize shifting of elements, it is possible to provide a fallback to cms block with predictable sizes.

First of all, it is required to implement logic for CmsBlock container to render provided placeholder while it is fetching content.

Lets create logic for handling loading state, so it will be indicating while block is loading to render placeholder.

// Declaring loading state for cms block
state = {
    ...
    isLoading: true
};

_getCmsBlock() {
		...;

    this.fetchData(...);
		// Setting loading flag for component to false
		this.setState({ isLoading: false });
}

Now it is also required to create new property for container and call it placeholder. This property is a function, that should return JSX element and by default it will be returning null. For each cms block it should have its own placeholder, because mostly cms blocks are unique and have its own length of content, so if there is no provided placeholder for it, better to not render placeholder to not make unpredictable layout shifts.

// CmsBlock.container

// Setting default property value for placeholder
static defaultProps = {
    ...,
    placeholder: () => null
};

// Passing placeholder and loading flag properties to CmsBlock component
containerProps() {
    const { ..., placeholder } = this.props;
		const { ..., isLoading } = this.state;

    return {
        ...,
        placeholder,
				isLoading
    };
  }

Now in CmsBlock component has to render placeholder:

render() {
        const {
            ...,
						placeholder,
            isLoading
        } = this.props;

				if (isLoading) {
            return placeholder();
        }

        ...;
    }
}

Now CmsBlock component supports rendering of placeholder while fetching content.

2. Providing a placeholder for dynamic block

In CategoryDetails component it is required to create a placeholder and provide it to CmsBlock component as property.

// Declaring render function for placeholder
renderCmsBlockFallback() {
    return <div block="CategoryDetails" elem="CmsPlaceholder" />;
}

render() {
    return (
        <article block="CategoryDetails">
            <div block="CategoryDetails" elem="Description">
                ...
                <CmsBlock
										identifier="..."
										// Providing placeholder for cms block
										placeholder={ this.renderCmsBlockFallback() }
								/>
            </div>
				...
        </article>
    );
}

Now it is needed to provide styles for created placeholder. In this case, since dimensions are unknown before block is rendered, it is possible to get dimensions from final result block.

Untitled

Since it is known that result block will be with height 40px and width is stretched till parent block end, it can be assumed that styles for placeholder will be next:

.CategoryDetails {
    &-CmsPlaceholder {
				// Setting dimensions of placeholder
        height: 40px;
				width: 100%;

				// Setting simple placeholder animation
        background-image: var(--placeholder-image);
        background-size: var(--placeholder-size);
        animation: var(--placeholder-animation);
    }
}

Now when cms block is loading, there is placeholder. When it is replaced, there is no shifting for ProductList component.

Peek 2023-02-06 09-01.gif

Styles for dynamic block placeholder should be adjusted depending on changing content of block, it is not required to provide pixel perfect sizes, main goal is to minimize shifting of blocks when dynamic block will replace placeholder.

<aside> 🎉 Now you know how to handle layout shifts caused by dynamic blocks!

</aside>