Sitecore-Analytics
Composable DXP MACH Sitecore Tips & Tricks

Tracking analytics via Sitecore Headless Services

The Sitecore Experience Platform is a true Headless Content Management System and it separates the building blocks managed by back-end components from the front-end, with this, you can create the Head of your application in any tech stack without compromising the Sitecore Content Management System offerings, and one of the major requirements or functionality or offering of Sitecore Experience Platform is Tracking of User Journey.

Sitecore is an API-first CMS and Decoupled CMS which also facilitate the Sitecore Experience Platform offering for digital marketers related to analytics and reporting on Headless CMS implementation.

When you are doing Sitecore Headless CMS-based implementation using any Client-Side Scripting framework like NextJS, tracking of events would be different from Sitecore Experience Platform ASP.NET MVC-based implementation.

For the Sitecore JSS NextJS-based application, we have to use the Sitecore JSS Tracking API, a client-side implementation of Headless Services Tracker and available to the Sitecore NextJS-based application via the Sitecore JSS tracking module.

Follow these links to skip to specifc topics

Sitecore JSS Tracking Details

Available Events

Prerequisite

Implementation

Sitecore Tracking Function Usage

The sitecore-jss-tracking package provides the trackEvent function to capture the following Sitecore Tracking Services:

Event
Goal
Outcome
Campaign
PageView

The method details are present at JSS Tracking API (sitecore.com)

Even type

Event properties

GoalInstance

{
    goalId: "<goal ID>"
}

OutcomeInstance

{
    outcomeId: "<goal ID>", 
    currencyCode: "<currency code>",
    monetaryValue: "<monetary value>"
}

PageViewInstance

{
    pageId: "<page ID>",
    url: "<page URL>"
}

CampaignInstance

{
    campaignId: "<campaign ID>"
}

EventInstance

{
    eventId: "<event-value>"
}
The prerequisite to use the Sitecore Headless Tracking Service is to enable the Sitecore Tracking for your Sitecore instance, by default it’s disabled:

<configuration>
  <sitecore>
    <settings>
      <setting name="Sitecore.JSS.TrackerServiceEnabled" value="true" />
    </settings>
  </sitecore>
</configuration>

You can use Sitecore Patch Configuration Files to deploy to both CM and CD roles.

The ways to access the different tracking services mentioned at Track events in JSS apps using the Tracking API (sitecore.com).

To simplify, I have created the file which will help to pass the required details for different types of operations.

Sitecore JSS Tracking code ? SitecoreTracking.ts

/*!***************************************************!*\
  !***    Siteocre JSS Tracking Functions          ***!
  \***************************************************/
/**
 This file created with the help of Sitecore StyleguideTracking details.
 The functions in this file can be used in NextJS based application with Sitecore JSS 
 to track the Sitecore Goals, Sitecore Outcome, Sitecore Page Event, Sitecore Campaign.
*/
import { trackingApi } from "@sitecore-jss/sitecore-jss-tracking"
import config from "temp/config"
import { dataFetcher } from "src/lib/data-fetcher"
import getConfig from "next/config"
import { useCallback } from "react"
import { useQueryClient } from "react-query"
import { AxiosError } from "axios"

const {
  publicRuntimeConfig: { PublicUrl },
} = getConfig()

const trackingApiOptions = {
  host: PublicUrl + "/api/services",
  serviceUrl: "/jss/track",
  querystringParams: {
    sc_apikey: config.sitecoreApiKey,
  },
  fetcher: dataFetcher,
}

interface SitecoreTracking {
  //--goalDetails: Goal GUID or Name
  triggerGoal: (goalDetails: string) => void
  triggerCampaign: (campaignDetails: string) => void
  triggerEvent: (eventDetails: string) => void
  triggerPageView: (pageView: PageView) => void
  triggerOutcome: (outcome: Outcome) => void
}

type PageView = {
  pageId: string
  url: string
}

type Outcome = {
  url: string
  pageId: string
  outcomeId: string
  monetaryValue: number
}

export const sitecoreTracking = (): SitecoreTracking => {
  const queryClient = useQueryClient()

  const triggerGoal = useCallback(
    (goalDetails: string) => {
      trackingApi
      .trackEvent([{ goalId: goalDetails }], trackingApiOptions)
      .catch((error) => {
        if (
          error &&
          error.isAxiosError &&
          (error as AxiosError).response?.status === 400
        ) {
        }
        console.error("Error :: SitecoreTracking >  triggerGoal=>" + error, error.isAxiosError)
      })
    },
    [queryClient]
  )

  const triggerCampaign = useCallback(
    (campaignDetails: string) => {
      trackingApi
      .trackEvent(
        [
          {
            campaignId: campaignDetails,
          },
        ],
        trackingApiOptions
      )
      .catch((error) => {
        if (
          error &&
          error.isAxiosError &&
          (error as AxiosError).response?.status === 400
        ) {
        }
        console.error(
          "Error :: SitecoreTracking >  triggerCampaign =>" + error,
          error.isAxiosError
        )
      })
    },
    [queryClient]
  )

  const triggerEvent = useCallback(
    (eventDetails: string) => {
      trackingApi
      .trackEvent(
        [
          {
            eventId: eventDetails
          },
        ],
        trackingApiOptions
      )
      .catch((error) => {
        if (
          error &&
          error.isAxiosError &&
          (error as AxiosError).response?.status === 400
        ) {
        }
        console.error(
          "Error :: SitecoreTracking >  triggerEvent =>" + error,
          error.isAxiosError
        )
      })
    },
    [queryClient]
  )  

  const triggerPageView = useCallback(
    (pageView: PageView) => {
      trackingApi
      .trackEvent(
        [
          {
            pageId: pageView.pageId,
            url: pageView.url,          },
        ],
        trackingApiOptions
      )
      .catch((error) => {
        if (
          error &&
          error.isAxiosError &&
          (error as AxiosError).response?.status === 400
        ) {
        }
        console.error(
          "Error :: SitecoreTracking >  triggerPageView =>" + error,
          error.isAxiosError
        )
      })
    },
    [queryClient]
  )

  const triggerOutcome = useCallback(
    (outcome: Outcome) => {
      trackingApi
      .trackEvent(
        [
          {
            url: outcome.url,
            pageId: outcome.pageId,
            outcomeId: outcome.outcomeId,
            currencyCode: "USD",
            monetaryValue: outcome.monetaryValue,       
          },
        ],
        trackingApiOptions
      )
      .catch((error) => {
        if (
          error &&
          error.isAxiosError &&
          (error as AxiosError).response?.status === 400
        ) {
        }
        console.error(
          "Error :: SitecoreTracking >  triggerPageView =>" + error,
          error.isAxiosError
        )
      })
    },
    [queryClient]
  )
  

  return {
    triggerGoal,
    triggerCampaign,
    triggerEvent,
    triggerPageView,
    triggerOutcome
  }
}

In the above functions, I have utilized the useCallback to improve the performance of API calls. The React hook useCallback hook will cache the API details and will only be called again when one of its dependencies changed. This will improve the performance.

You can pass dependencies as per your need to React hook useCallback

useCallback(fn, deps) – Returns a memoized callback.

Usage of Sitecore JSS Tracking code ? Sitecore-Tracking-Component.tsx

/*!***************************************************!*\
  !***    Siteocre JSS Tracking Functions Usage         ***!
  \***************************************************/
/**
 The purpose of this file is to showcase, how Sitecore JSS Tracking services can be used 
 from Sitecore NextJS applicaiton to track the Sitecore Goals, Sitecore Outcome, Sitecore Page Event, Sitecore Campaign.
*/
import { Box } from "@material-ui/system"
import { sitecoreTracking } from "lib/SitecoreTracking"
import { useState } from "react"

const SitecoreTracking = () => {
  const { triggerGoal,triggerCampaign,triggerEvent, triggerPageView, triggerOutcome } = sitecoreTracking()
  const [inputGoalValue, setGoalValue] = useState('')
  const [inputCampaignValue, setCampaignValue] = useState('')
  const [inputEventValue, setEventValue] = useState('')
  const [inputPageIdValue, setPageIdValue] = useState('')
  const [inputPageUrlValue, setPageUrlValue] = useState('')
  const [inputOutComeValue, setOutComeValue] = useState('')
  const [inputMonetaryValue, setMonetaryValue] = useState('')
  return (
    <Box>
      <Box>
          <fieldset className="form-group col-sm">
              <legend>Goal</legend>
              <p>
                Goals are defined in{" "}
                <code>/sitecore/system/Marketing Control Panel/Goals</code>
              </p>
              <label htmlFor="goal">Goal GUID or Name</label>
              <input
                type="text"
                className="form-control"
                id="goal"
                placeholder="i.e. Register"
                value={inputGoalValue}
                onChange={e => { setGoalValue(e.currentTarget.value); }}
              />
              <button
                type="button"
                className="btn btn-primary mt-3"
                onClick={() => triggerGoal(inputGoalValue)} 
              >
                Submit
              </button>
          </fieldset>

          <fieldset className="form-group col-sm">
                <legend>Campaign</legend>
                <p>
                  Campaigns are defined in{" "}
                  <code>
                    /sitecore/system/Marketing Control Panel/Campaigns
                  </code>
                </p>
                <label htmlFor="campaign">Campaign GUID or Name</label>
                <input
                  type="text"
                  className="form-control"
                  id="campaign"
                  value={inputCampaignValue}
                  onChange={e => { setCampaignValue(e.currentTarget.value); }}
                />
                <button
                  type="button"
                  className="btn btn-primary mt-3"
                  onClick={() => triggerCampaign(inputCampaignValue)} 
                >
                  Submit
                </button>
              </fieldset> 

              <fieldset className="form-group col-sm">
                <legend>Event</legend>
                <p>
                  Events are defined in{" "}
                  <code>/sitecore/system/Settings/Analytics/Page Events</code>
                </p>
                <label htmlFor="event">Event GUID or Name</label>
                <input
                  type="text"
                  id="event"
                  className="form-control"
                  value={inputEventValue}
                  onChange={e => { setEventValue(e.currentTarget.value); }}
                />
                <button
                  type="button"
                  className="btn btn-primary mt-3"
                  onClick={() => triggerEvent(inputEventValue)} 
                >
                  Submit
                </button>
              </fieldset>

              <fieldset className="form-group col-sm">
                <legend>Page View</legend>
                <p>
                  Track arbitrary page views for custom routing or offline use.
                  Note that Layout Service tracks page views by default unless{" "}
                  <code>tracking=false</code> is passed in its query string.
                </p>
                <label htmlFor="pageId">Page Item GUID</label>
                <input
                  type="text"
                  className="form-control"
                  id="pageId"
                  placeholder="i.e. {11111111-1111-1111-1111-111111111111}"
                  value={inputPageIdValue}
                  onChange={e => { setPageIdValue(e.currentTarget.value); }}
                />
                <br />
                <label htmlFor="pageUrl">Page URL</label>
                <input
                  type="text"
                  className="form-control"
                  id="pageUrl"
                  placeholder="i.e. /foo/bar"
                  value={inputPageUrlValue}
                  onChange={e => { setPageUrlValue(e.currentTarget.value); }}
                />
                <button
                  type="button"
                  className="btn btn-primary mt-3"
                  onClick={() => triggerPageView({pageId: inputPageIdValue, url: inputPageUrlValue})} 
                >
                  Submit
                </button>
              </fieldset>      

              <fieldset className="form-group col-sm">
                <legend>Outcome</legend>
                <p>
                  Outcomes are defined in{" "}
                  <code>/sitecore/system/Marketing Control Panel/Outcomes</code>
                </p>
                <label htmlFor="pageId">Page Item GUID</label>
                <input
                  type="text"
                  className="form-control"
                  id="pageId"
                  placeholder="i.e. {11111111-1111-1111-1111-111111111111}"
                  value={inputPageIdValue}
                  onChange={e => { setPageIdValue(e.currentTarget.value); }}
                />
                <br />
                <label htmlFor="pageUrl">Page URL</label>
                <input
                  type="text"
                  className="form-control"
                  id="pageUrl"
                  placeholder="i.e. /foo/bar"
                  value={inputPageUrlValue}
                  onChange={e => { setPageUrlValue(e.currentTarget.value); }}
                />
                <br />
                <label htmlFor="outcomeName">Outcome GUID or Name</label>
                <input
                  type="text"
                  className="form-control"
                  id="outcomeName"
                  value={inputOutComeValue}
                  placeholder="i.e. Marketing Lead"
                  onChange={e => { setOutComeValue(e.currentTarget.value); }}
                />
                <br />
                <label htmlFor="outcomeValue">Monetary Value (optional)</label>
                <input
                  type="number"
                  className="form-control"
                  id="outcomeValue"
                  value={inputMonetaryValue}
                  placeholder="i.e. 1337.00"
                  onChange={e => { setMonetaryValue(e.currentTarget.value); }}
                />
                <button
                  type="button"
                  className="btn btn-primary mt-3"
                  onClick={() => triggerOutcome({url: inputPageUrlValue, pageId: inputPageIdValue,outcomeId: inputOutComeValue,monetaryValue:parseInt(inputMonetaryValue) })} 
                >
                  Submit
                </button>
              </fieldset>


      </Box>
    </Box>
  )
}

export default SitecoreTracking

In the code above, block (NextJS Component), I tried to use all the Sitecore JSS Tracking functions defined at SitecoreTracking.ts:

Execute Goal by Goal Name

Execute Goal by Goal Id

Tracking object not deployed

If you are trying to execute Goal or Events or any other tracking events on objects or Sitecore Tracking Deployed which not present in the Sitecore Content Tree (Web DB) then you will get error MarketingDefinitionNotFound

For this you can follow the steps at Deploy marketing definitions and taxonomies (sitecore.com) or verify the Sitecore Items at Web DB

Execute Campaign by Campaign Name

Execute Campaign by Campaign Id

Execute Page Event

Execute Page View

Execute Outcome

Headless CMS architecture separates back-end content functions (like creation, management, and storage) from front-end functions (like presentation and delivery) in this case you can utilize Sitecore Tracking Services to execute the Sitecore Goals, Sitecore Outcome, Sitecore Page Event, Sitecore Campaign in order to track the performance of Sitecore Website content.

Later the collected data via the Sitecore xConnect API can be used to build reports for business users.

Credit: Sitecore

Credit/References

Amit is an IT Solution Architect with Assurex. Reach out to Amit on

Did you find this useful? If so, please share with your connections.

Leave a Reply

Your email address will not be published. Required fields are marked *