Enhanced eCommerce in PWA Studio
Tags: javascript, magento, analytics
Published: 2021-01-18 21:24:20

This blog post is mainly aimed at Magento agencies building a mono-repo around PWA Studio and having a need to make an easy yet extensible way to add Enhanced eCommerce.

>Note that if you don't have a mono repo setup like PWA Studio you won't need the React Context

**Overview**:
* Adding Google Tag Manager
* Creating a React Context
* Setting up a basic data layer file
* Implementing the data layer methods

### Adding Google Tag Manager
Add the code below, make sure to add your own GTM code.
I'd advice you to make the GTM code variable so you can connect to multiple containers based on your build/pipeline etc.
```
//template.html (the inside your storefront)
<script>
    dataLayer = [];
</script>
<!-- Google Tag Manager -->
<script>
    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
        j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
        'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXX');
</script>
<!-- End Google Tag Manager -->
```
### Creating a React Context
Since we are working inside a mono-repo we want to be able to customise what data we send to the data layer per customer that's build upon the mono-repo.

So firstly locate the position where you'd normally would create context's inside your project.

Add the following files:
`context/dataLayer/index.js`
`context/dataLayer/dataLayer.js`
`context/dataLayer/data.js`

```
//context/dataLayer/index.js
export { DataLayerProvider, useDataLayerConfig } from './dataLayer';
```

```
//context/dataLayer/dataLayer.js

import React, { useContext } from 'react';
import { node, object } from 'prop-types';
import { merge } from 'lodash-es';
import { dataLayerConfig } from './data';

export const DataLayer = React.createContext(null);

const defaultDataLayer = dataLayerConfig;

export const DataLayerProvider = ({ children, additionalDataLayer = {} }) => {
    return (
        <DataLayer.Provider
            value={merge({}, defaultDataLayer, additionalDataLayer)}
        >
            {children}
        </DataLayer.Provider>
    );
};

export const useDataLayerConfig = () => useContext(DataLayer);

DataLayerProvider.propTypes = {
    children: node.isRequired,
    query: object,
    additionalDataLayer: object,
};

```

```
//context/dataLayer/data.js
export const dataLayerConfig = {
    reset: {
        product: () => ({
            ecommerce: undefined,
            items: undefined,
        }),
        category: () => ({
            ecommerce: undefined,
            items: undefined,
        }),
    },
    product: data => ({
        event: 'virtualPageview',
        pagePath: window.location.href,
        pageTitle: `${data.name} - ${STORE_NAME}`,
        pageType: 'product',
        items: [
            {
                id: data.sku,
            },
        ],
        ecommerce: {
            detail: {
                products: [
                    {
                        name: data.name,
                        id: data.sku,
                    },
                ],
            },
        },
    }),
    cms: data => ({
        event: 'virtualPageview',
        pagePath: window.location.href,
        pageTitle: `${data.title} - ${STORE_NAME}`,
        pageType: 'cms',
    }),
    category: data => ({
        event: 'virtualPageview',
        pagePath: window.location.href,
        pageTitle: `${data.category.name} - ${STORE_NAME}`,
        pageType: 'category',
    }),
    removeFromCart: data => ({
        event: 'removeFromCart',
        ecommerce: {
            remove: {
                products: [
                    {
                        name: data.product.name,
                        id: data.product.sku,
                        quantity: data.quantity,
                    },
                ],
            },
        },
    }),
    addToCart: data => ({
        event: 'addToCart',
        items: [
            {
                id: data.sku,
            },
        ],
        ecommerce: {
            add: {
                products: [
                    {
                        name: data.name,
                        id: data.sku,
                        quantity: data.quantity,
                    },
                ],
            },
        },
    }),
};

```
>Note that this a really small example file, you should add more methods like submitOrder, checkoutSuccess, checkout, notFound etc etc

Make sure to import the Context into the storefront and add it to the provider tree.
```
//index.js (the on in your storefront)
<AppContextProvider>
  <DataLayerProvider>
    ...
  </DataLayerProvider>
</AppContextProvider>

```


### Implementing the data layer methods
So we've set up the basic of this solution