Edit in GitHubLog an issue

Shimmer

The Shimmer component is a loading indicator that takes the shape of the component being loaded. Instead of blocking the entire page like a traditional full-screen loader, Shimmer loaders are component-shape specific to show users previews of what's loading on the page.

These previews improves the perceived performance of the app and prevents CLS (Content Layout Shift). The Shimmer component eliminates most of the CLS on a page, which helps improve Google Lighthouse scores.

Parameters

NameTypeDescription
classesObjectStyles to apply to the root of the Shimmer. Available classes are root and root_[TYPE].
borderRadiusstring | numberBorder radius for the shimmer.
heightstring | numberSets the height of the Shimmer. Numbers are in rem units. Strings are used directly (Example: '100px').
widthstring | numberSets the width of the Shimmer. Numbers are in rem units. Strings are used directly (Example: '100px').
styleObjectCSS styles to apply to the Shimmer.
type'rectangle' | 'button' | 'checkbox' | 'radio' | 'textArea' | 'textInput'The base element shape to apply to the Shimmer.
childrennodeChildren to output within the Shimmer. Useful for setting image placeholders.

Properties

NameTypeDescription
classesObjectis an object containing the class names for the Shimmer component.
classes.rootstringis the class for the container
classes.root_rectanglestringis the class for the container of type rectangle
classes.root_buttonstringis the class for the container of type button
classes.root_checkboxstringis the class for the container of type checkbox
classes.root_radiostringis the class for the container of type radio
classes.root_textAreastringis the class for the container of type textArea
classes.root_textInputstringis the class for the container of type textInput
borderRadiusnumber | stringis the border radius of the Shimmer
heightnumber | stringis the height of the Shimmer
widthnumber | stringis the width of the Shimmer
styleObjectis an object of inline styles
typestringis the type of the Shimmer
childrennodeare the children of the Shimmer

Source Code: pwa-studio/packages/venia-ui/lib/components/Shimmer/shimmer.js

Shimmer for Components#

When loading data, previously we would return null (or a full-screen loader) instead of the actual component. We can now return a shimmer version of the component, which will take up the same space without relying on data.

Direct use of the Shimmer component within normal components should be avoided when possible. If the shimmer is replacing a component that is imported, a .shimmer.js file should be created for that imported component in its folder and exported in its index.js.

Example#

There are 4 critical files for creating a Shimmer component:

  • Main.js - the main component that has the loading status and would usually return null for the component while loading
  • SubComponent/index.js - Previously would only export the main component. Must now export named variable for shimmer component
  • SubComponent/subComponent - Same SubComponent as usual
  • SubComponent/subComponent.shimmer.js - .shimmer.js extension is used for easily identifying that it's a shimmer and the component it's attached to. It can contain complex arrangement of base Shimmer elements, or include other subcomponents Shimmers.

Main.js

Copied to your clipboard
1import React from 'react';
2import SubComponent, { SubComponentShimmer } from '../path/to/SubComponent';
3// ....
4export default () => {
5 const { data, isLoading } = fetchData();
6
7 if (isLoading) {
8 return (
9 <SubComponentShimmer />
10 );
11 }
12
13 if (!data) {
14 return 'No data';
15 }
16
17 return (
18 <SubComponent someValue={data} />
19 );
20};
21// ....

../path/to/SubComponent/index.js

Copied to your clipboard
1export { default } from './subComponent.js';
2// Export named shimmer component
3export { default as SubComponentShimmer } from './subComponent.shimmer.js';

../path/to/SubComponent/subComponent.js

Copied to your clipboard
1import React from 'react';
2import { shape, string } from 'prop-types';
3import { useStyle } from '../../path/to/classify';
4import defaultClasses from './subComponent.css';
5const SubComponent = (props) => {
6 const classes = useStyle(defaultClasses, props.classes);
7 const { someValue } = props;
8
9 return (
10 <div className={classes.root}>
11 <div className={classes.item}>{someValue}</div>
12 </div>
13 );
14};
15SubComponent.defaultProps = {
16 classes: {}
17};
18SubComponent.propTypes = {
19 classes: shape({
20 root: string,
21 item: string
22 })
23}
24export default SubComponent;

../path/to/SubComponent/subComponent.shimmer.js

Copied to your clipboard
1import React from 'react';
2import { useStyle } from '../../path/to/classify';
3import Shimmer from '../path/to/base/Shimmer';
4import defaultClasses from './subComponent.css'; // Load same classes as real SubComponent
5const SubComponentShimmer = (props) => {
6 // Important to still merge-in prop classes for extensibility/targetability
7 const classes = useStyle(defaultClasses, props.classes);
8
9 return (
10 <div className={classes.root}>
11 <Shimmer className={classes.item} />
12 </div>
13 );
14};
15SubComponentShimmer.defaultProps = {
16 classes: {}
17};
18SubComponentShimmer.propTypes = {
19 classes: shape({
20 root: string,
21 item: string
22 })
23}
24export default SubComponentShimmer;

Adjusting existing Shimmers#

When you make layout changes to a Shimmer's parent component, you should also adjust the Shimmer component to match. In this example, we'll add a custom attribute shimmer to the detail section of the product page.

local-intercept.js

Copied to your clipboard
1const { Targetables } = require('@magento/pwa-buildpack');
2const targetables = Targetables.using(targets);
3const productShimmerComponent = targetables.reactComponent(
4 '@magento/venia-ui/lib/RootComponents/Product/product.shimmer'
5);
6
7/**
8 * As a best practice, you should create a separate Shimmer file for the new attribute and import it into the
9 * productShimmerComponent. But for simplicity, we'll inline the jsx as shown here.
10 */
11productShimmerComponent.appendJSX(
12 'section className={classes.details}'
13 `<div className={classes.detailsTitle}>
14 <Shimmer width="100%" height={1} />
15 </div>
16 <Shimmer width="100%" height={1} />`
17);

Accessibility#

To maintain accessibility for screen readers, we can pass aria-live="polite" aria-busy="true" to the Shimmer component (or an element that wraps the Shimmer(s) in a more complex instance).

It's important to then add aria-live="polite" aria-busy="false" to the normal component that replaces the shimmer.

Example#

Copied to your clipboard
1// ....
2import Shimmer from '../path/to/base/Shimmer';
3// ....
4export default () => {
5 // ....
6 return (
7 <Shimmer />
8 );
9};
  • Privacy
  • Terms of Use
  • Do not sell my personal information
  • AdChoices
Copyright © 2022 Adobe. All rights reserved.