Yotpo SMS & Email Web Pixel Installation Guide for Shopify Hydrogen 2

      Yotpo SMS & Email Web Pixel Installation Guide for Shopify Hydrogen 2


        Article Summary

        Products


        SMS & Email
        Supported plans

        Free, Starter, Pro, Premium, Enterprise

        eCommerce Platform

        Shopify, Shopify Plus

        Use this repository as a guideline for implementing Yotpo SMS customer behavior tracking on Shopify Hydrogen 2 storefronts that are using the Remix.run framework.

        Before you proceed, please make sure you have the Yotpo SMS & Email (SMSBump) sales channel installed on your Shopify store.

        Dependencies

        Make sure you have the @shopify/hydrogen-react library added to your dependencies. We will use it to load the JS SDK script.

        Analytics layer in Hydrogen 2

        The examples shown here are based on the recommended approach of collecting analytics, as described in this Shopify article.

        At the time of writing this guide, the usePageAnalytics() hook is not shipped out of the box when you create a hydrogen project using npm create @shopify/hydrogen.

        Make sure you add it to app/utils.js:

        import { useMatches, useMemo } from '@remix-run/react'
        
        export function usePageAnalytics() {
          const matches = useMatches();
        
          const analyticsFromMatches = useMemo(() => {
            const data = {};
        
            matches.forEach((event) => {
              const eventData = event?.data;
              if (eventData) {
                eventData['analytics'] && Object.assign(data, eventData['analytics']);
              }
            });
        
            return data;
          }, [matches]);
        
          return analyticsFromMatches;
        }
        Warning:

        Although the usePageAnalytics() hook is provided for thoroughness, it's generally a good idea to check the original guide for an updated version.

        useYotpoSms() hook and context key constants

        Create an app/analytics/yotposms.js.

        import { useLoadScript } from "@shopify/hydrogen-react";
        import { useEffect, useRef } from "react";
        
        export const YOTPOSMS_ANALYTICS_PAGEVIEW = 'yotpoSmsPageview'
        export const YOTPOSMS_ANALYTICS_EVENT = 'yotpoSmsEvent'
        
        export default function useYotpoSms(opts = {}) {
            const dataLayer = useRef([])
        
            Object.assign(opts, {
                platform: 2,
            })
        
            useLoadScript('https://d18eg7dreypte5.cloudfront.net/browse-abandonment/v2/generic.js')
        
            useEffect(() => {
                window.wtba = window.wtba || []
        
                if (Array.isArray(dataLayer.current) && dataLayer.current.length) {
                    window.wtba.push(...dataLayer.current)
                }
        
                dataLayer.current = window.wtba
                dataLayer.current.push({ type: 'config', options: opts })
            }, [])
        
            return dataLayer
        }

        It provides 3 exports:

        • the useYotpoSms( options ) hook, which will load the SDK script and expose the data layer object
        • YOTPOSMS_ANALYTICS_PAGEVIEW constant 
        • YOTPOSMS_ANALYTICS_EVENT constant 

        Update routes and actions

        You need to update your routes and actions so they can start broadcasting analytics data.

        Homepage route

        import { YOTPOSMS_ANALYTICS_PAGEVIEW } from '~/analytics/yotposms';

        Update your json()/defer() within the loader() of app/routes/_index.jsx, so that it looks as follows:

          return defer({
            featuredCollection,
            recommendedProducts, 
        +   analytics: {
        +     [YOTPOSMS_ANALYTICS_PAGEVIEW]: {
        +       type: 'home',
        +     }
        +   }
          });

        Update AddToCartButton component

        If you followed Shopify’s guide on tracking add-to-cart events, then you should have the AddToCartAnalytics Higher Order Component. We want to use the useYotpoSms() hook there so we can push those events to the data layer. 

        In your app/components/AddToCartButton.jsx:

        import { useRouteLoaderData } from "@remix-run/react";
        import useYotpoSms, { YOTPOSMS_ANALYTICS_EVENT } from "~/analytics/yotposms";
        
        function AddToCartAnalytics({
            fetcher,
            children,
        }) {
            // Data from action response
            const fetcherData = fetcher.data;
            // Data in form inputs
            const formData = fetcher.formData;
        +   const rootData = useRouteLoaderData('root');
        +
        +   const yotpoDataLayerRef = useYotpoSms({
        +       store: rootData.publicStoreDomain,
        +   })
        +
            useEffect(() => {
                if (formData) {
                    const cartData = {};
                    const cartInputs = CartForm.getFormInput(formData);
        
                    try {
                        // Get analytics data from form inputs
                        if (cartInputs.inputs.analytics) {
                            const dataInForm = JSON.parse(
                                String(cartInputs.inputs.analytics),
                            );
                            Object.assign(cartData, dataInForm);
                        }
                    } catch {
                        // do nothing
                    }
        
                    // If we got a response from the add to cart action
                    if (Object.keys(cartData).length && fetcherData) {
                        // send add to cart event
        +               cartData.products?.forEach(event => {
        +                   if (event && event[YOTPOSMS_ANALYTICS_EVENT]) {
        +                       yotpoDataLayerRef.current.push(event[YOTPOSMS_ANALYTICS_EVENT])
        +                   }
        +               })
                    }
                }
            }, [fetcherData, formData]);
            return <>{children}</>;
        }

        Product route

        import { YOTPOSMS_ANALYTICS_EVENT, YOTPOSMS_ANALYTICS_PAGEVIEW } from '~/analytics/yotposms';

        Update your json()/defer() within loader() of app/routes/products.$handle.jsx, so that it looks as follows:

          return defer({
            product,
            variants,
        +   analytics: {
        +     [YOTPOSMS_ANALYTICS_PAGEVIEW]: {
        +       type: 'product',
        +       id: product.id.replace('gid://shopify/Product/', ''),
        +       variant_id: product.selectedVariant.id.replace('gid://shopify/ProductVariant/', ''),
        +     }
        +   }
          });

        In order to receive analytics on products added to cart, add the productAnalytics prop to your <AddToCartButton/> component.

         <AddToCartButton
            // existing props, 
        +   productAnalytics={{
        +       [YOTPOSMS_ANALYTICS_EVENT]: {
        +           type: 'event',
        +           event: 'product_added_to_cart',
        +           id: product.id.replace('gid://shopify/Product/', ''),
        +           variant_id: selectedVariant.id.replace('gid://shopify/ProductVariant/', ''),
        +       }
        +   }}
        >
            {selectedVariant?.availableForSale ? 'Add to cart' : 'Sold out'}
         </AddToCartButton>

        Collection route

        import { YOTPOSMS_ANALYTICS_PAGEVIEW } from '~/analytics/yotposms';

        Update yourjson()/defer() within loader() oflapp/routes/collections.$handle.jsx, so it looks as follows:

        
          return json({
            collection,
        +   analytics: {
        +     [YOTPOSMS_ANALYTICS_PAGEVIEW]: {
        +       type: 'collection',
        +       id: collection.id.replace('gid://shopify/Collection/', ''),
        +     }
        +   }
          });

        Logout route

        We will set a flash session on customer logout to be able to notify the tracking script about this event.
        Update the action() handler of your app/routes/account_.logout.jsx, setting the session just before the redirect().

        + session.flash('customerJustLoggedOut', true)
        
          return redirect('/', {
            headers: {
              'Set-Cookie': await session.commit(),
            },
          });

        Account route

        We will use the account route to "catch" the current logged-in customer. This is purely illustrative and we do it this way here because customers are redirected to the profile page upon login, and we already have the customer object there. You can achieve the same on any other route if your use case differs.

        import { YOTPOSMS_ANALYTICS_EVENT } from '~/analytics/yotposms';

        Update your loader() within app/routes/account.jsx route, so that it looks as follows:

        const { customer } = await storefront.query(CUSTOMER_QUERY, {
              variables: {
                customerAccessToken: customerAccessToken.accessToken,
                country: storefront.i18n.country,
                language: storefront.i18n.language,
              },
              cache: storefront.CacheNone(),
            });
        
            if (!customer) {
              throw new Error('Customer not found');
            }
        
        +   const analytics = {}
        +
        +   Object.assign(analytics, {
        +     [YOTPOSMS_ANALYTICS_EVENT]: (({ email, phone }) => ({
        +       type: 'customer',
        +       event: 'customer_login',
        +       customer: { phone, email },
        +     }))(customer)
        +   })
        
            return json(
        -     { isLoggedIn, isPrivateRoute, isAccountHome, customer },
        +     { isLoggedIn, isPrivateRoute, isAccountHome, customer, analytics },
              {
                headers: {
                  'Cache-Control': 'no-cache, no-store, must-revalidate',
                },
              },
            );

        App Root (app/root.jsx)

        import { useLocation } from '@remix-run/react';
        import { useEffect, useMemo } from 'react';
        import useYotpoSms, { YOTPOSMS_ANALYTICS_EVENT, YOTPOSMS_ANALYTICS_PAGEVIEW } from './analytics/yotposms';
        import { usePageAnalytics } from './utils';

        In the loader(), after validating the customer access token, check for the flash logout session and pass it down to <App/> using json()/defer().

          // validate the customer access token is valid
          const {isLoggedIn, headers} = await validateCustomerAccessToken(
            customerAccessToken,
            session,
          );
        
          ...
        
        + const analytics = {}
        +
        + if (! isLoggedIn) {
        +   if (session.get('customerJustLoggedOut')) {
        +     Object.assign(analytics, {
        +       [YOTPOSMS_ANALYTICS_EVENT]: {
        +         type: 'customer',
        +         event: 'customer_logout',
        +       }
        +     })
        +
        +     headers.append('Set-Cookie', await session.commit())
        +   }
        + }
        
          return defer(
            {
              cart: cartPromise,
              footer: footerPromise,
              header: await headerPromise,
              isLoggedIn,
              publicStoreDomain,
        +     analytics,
            },
            {headers},
          );

        Lastly, update your<App /> component:

        export default function App() {
          const data = useLoaderData();
        
        + const yotpoDataLayerRef = useYotpoSms({
        +   store: data.publicStoreDomain,
        + })
        +
          const location = useLocation();
          const lastLocationKey = useRef('');
          const pageAnalytics = usePageAnalytics();
        
          useEffect(() => {
            // Filter out useEffect running twice
            if (lastLocationKey.current === location.key) return;
         
            lastLocationKey.current = location.key;
        +
        +   if (pageAnalytics[YOTPOSMS_ANALYTICS_PAGEVIEW]) {
        +     yotpoDataLayerRef.current.push(pageAnalytics[YOTPOSMS_ANALYTICS_PAGEVIEW])
        +   }
        +
        +   if (pageAnalytics[YOTPOSMS_ANALYTICS_EVENT]) {
        +     yotpoDataLayerRef.current.push(pageAnalytics[YOTPOSMS_ANALYTICS_EVENT])
        +   }
          }, [location, pageAnalytics]);
        
          return (
            <html lang="en">
            ...
            </html>
          )
        }
        

        Was this article helpful?