How to display short product descriptions on the Product Page with PWA Studio

We continue the series of articles with technical lifehacks, dedicated to PWA Studio. After the overview of how to Insert CMS Block on the Product Page, we suggest to check out some more interesting situations.

Imagine a store using the PWA Studio storefront where the Product Page should contain the product’s short description. You may see nothing unusual here, but there is a trick – the default Venia theme doesn’t show the product’s short description by default. Oops. But no worries, this is not difficult to fix. Thanks to PWA Studio’s extensibility framework.

In order to solve our task, we need to perform two major steps:

  1. Create a custom component that will show the product’s short description.
  2. Insert that component into Venia’s product page component.

Insert the Custom Component into the ProductFullDetail

The product’s short description will be inserted to the page after the product’s title. In the ProductFullDetail component, the title is wrapped by the section <section className={classes.title}>, which will be taken as a reference to insert the short description.

To make things work, we’ll add the adjustments into the project’s local-intercept.js file. Read what this file is about and how it works in the “Insert CMS Block on the Product Page right before footer” article.

// file: local-intercept.js

function localIntercept(targets) {  
    const {Targetables} = require('@magento/pwa-buildpack');  
    const targetables = Targetables.using(targets);

    // load the component to be customized
    const ProductFullDetailComponent = targetables.reactComponent(  
        '@magento/venia-ui/lib/components/ProductFullDetail/productFullDetail.js'  
    );

    // import the custom component in the component to be modified
    const ProductShortDescription = ProductFullDetailComponent.addImport(  
        "ProductShortDescription from '@theme/components/ProductShortDescription'"  
    );  

    // insert the custom component that renders product's short description
    ProductFullDetailComponent.insertAfterJSX(  
        '<section className={classes.title}>',  
        `<${ProductShortDescription} url_key={product.url_key} />`
    );
}  

module.exports = localIntercept;

It is important to highlight that our custom component receives the url_key as a property from the Venia’s ProductFullDetail component. It is not required from our side to do something to pass it [the property], because our custom component has been inserted into the ProductFullDetail component. This allows all required data (url_key) for our custom component to be present in product.url_key.

Also, you might notice that the ProductShortDescription component is imported from some @theme. That’s our project’s src directory. It is configured using webpack alias in the project’s webpack.config.js file. Below you can find the detailed instruction how to make it work.

Add Project’s src dir Alias in Webpack

To assure easier import of the components, we recommend adding the alias. That’s not obligatory, but a good thing to have. With these adjustments in place, we will avoid imports like:

import ProductShortDescription from '../../../../../../components/ProductShortDescription';

In order to add the alias, we shall incorporate following changes into webpack.config.js:

// file: webpack.config.js

// [...]

const path = require('path');

module.exports = async env => {
    // [...]

    config.resolve = {  
        ...config.resolve,  
        alias: {  
            ...config.resolve.alias,  
            '@theme': path.resolve(__dirname, 'src')  
        }  
    }

    // [...]

    return config;  
};

Custom Component

Now, we will work on the component that renders the data – Short Description.
It requires the product url_key to know for what exact product the short description will be shown. It uses a GraphQL query GET_PRODUCT_SHORT_DESCRIPTION that is passed down to the talon useProductShortDescription, which is responsible for fetching and providing the product’s short description.

// file: src/components/ProductShortDescription/productShortDescription.js

import React, { Fragment } from 'react';  
import { useProductShortDescription } from '@theme/talons/ProductShortDescription/useProductShortDescription';  
import { GET_PRODUCT_SHORT_DESCRIPTION } from './productShortDescription.gql';  
import { fullPageLoadingIndicator } from '@magento/venia-ui/lib/components/LoadingIndicator';  
import RichText from '@magento/venia-ui/lib/components/RichText';  
import { string } from 'prop-types';  

const ProductShortDescription = props => {  
    const { url_key } = props;  
    const talonProps = useProductShortDescription({  
        url_key,  
        getProductShortDescription: GET_PRODUCT_SHORT_DESCRIPTION  
  });  

    const {  
        error,  
        loading,  
        shortDescription  
  } = talonProps;  

    if (error && !shortDescription) {  
        return null;  
    }  

    if (loading) {  
        return fullPageLoadingIndicator  
  }  

    return (  
        <Fragment>  
            <RichText content={shortDescription} />  
        </Fragment>  
    );  
}  

ProductShortDescription.propTypes = {  
    url_key: string.isRequired  
}  

export default ProductShortDescription;
// file: file: src/components/ProductShortDescription/index.js

import ProductShortDescription from "./productShortDescription.js";  

export default ProductShortDescription;

GraphQL Query to Fetch Product’s Short Description

// file: src/components/ProductShortDescription/productShortDescription.gql.js

import { gql } from '@apollo/client';

export const GET_PRODUCT_SHORT_DESCRIPTION = gql`
    query getProductShortDescription($urlKey: String!) {
        products(filter: { url_key: { eq: $urlKey } }) {
            items {
                id
                url_key,
                short_description {
                    html
                }
            }
        }
    }
`;

Talon that Fetches the Product’s Short Description – useProductShortDescription

// file: src/talons/ProdutShortDescription/useProductShortDescription.js

import React, { useMemo } from 'react';
import { useQuery } from '@apollo/client';
import { string, func } from 'prop-types';

export const useProductShortDescription = props => {
    const {
        url_key,
        getProductShortDescription
    } = props;

    const { error, loading, data } = useQuery(getProductShortDescription, {
        fetchPolicy: 'cache-and-network',
        nextFetchPolicy: 'cache-first',
        variables: {
            urlKey: url_key
        }
    });

    const shortDescription = useMemo(() => {
        if (!data || !data.products || !data.products.items) {
            return null;
        }

        const product = data.products.items.find(
            item => item.url_key === url_key
        );

        if (!product) {
            return null;
        }

        return product.short_description.html;
    }, [data, url_key]);

    return {
        data,
        shortDescription,
        error,
        loading
    }
}

useProductShortDescription.propTypes = {
    url_key: string.isRequired,
    getProductShortDescription: func.isRequired
}

That’s it! These are all the required changes to insert product’s short description in PWA Studio under its title on the product details page.

Using the same approach, it is possible to add any other information to the product page, like displaying some custom product attributes, for example. Of course, some adjustments are needed, but that’s not a big deal when almost everything is prepared.

Read also: Magento PWA Plugins

Good luck! And do not hesitate to let your friends and teammates know what you’ve just learned ;)