Take promotions to the next level with our new Rules Engine.

Tutorials

Build a Commerce Layer Auth React Component

June 25, 2025 Alex Canessa

When working with Commerce Layer’s APIs, you need an access token scoped to a specific market. All API responses will then reflect the context of that market, for example, prices, stock levels, and so on.

If you’ve been using the Commerce Layer React Components, you know that they only work when wrapped inside the CommerceLayer provider:

<CommerceLayer accessToken="my-access-token">
	{/* your code */}
</CommerceLayer>

For most of my projects, I create a wrapper component that handles retrieving and storing the token, and then passes it down to the CommerceLayer provider. Let’s build it together.

The CommerceLayerAuth Component

Before writing any code, I like to think about the component’s props and types. In this case, I want to pass down the data required to fetch the token (i.e. clientId, slug, and market), which will be retrieved as an environment variable.

I’ll also define the AuthenticateReturn type from client_credentials and store it in state and local storage.

import { ReactNode } from "react";

export type CommerceLayerAuthProps = {
  children: ReactNode;
  clientId: string;
  slug: string;
  market: string;
};

Building the Component

Once we have the types, we can focus on the core logic: fetching the token using the authenticate function from @commercelayer/js-auth, storing it in state, and passing it to the CommerceLayer provider.

Let’s install the dependencies first:

pnpm install @commercelayer/js-auth @commercelayer/react-components

And now we can create the skeleton of the component, getting the token on component mounting - leaving the storing functionality out for now.

import { ReactNode, useEffect, useState } from "react";
import { authenticate, AuthenticateReturn } from "@commercelayer/js-auth";

const CommerceLayerAuth = ({
  children,
  clientId,
  slug,
  market,
}: CommerceLayerAuthProps) => {
  const [authentication, setAuthentication] = useState<Authentication | null>(
    null
  );

  useEffect(() => {
    const fetchAuthentication = async () => {
      const auth = await authenticate("client_credentials", {
        clientId,
        scope: `market:id:${market}`,
      });

      setAuthentication(auth);
    };

    fetchAuthentication();
  }, [clientId, market]);

  if (!authentication) {
    return null;
  }

  return (
    <CommerceLayer
      accessToken={authentication.accessToken}
      endpoint={`https://${slug}.commercelayer.io`}
    >
      {children}
    </CommerceLayer>
  );
};

Understanding the Token (AuthenticateReturn)

To better understand what to store and use from the response that Commerce Layer gives us when authenticating, I want to have a look at the payload. And we can appreciate that, besides using the token itself, we can also leverage the expires property to validate whether the token is still valid or needs to be refreshed.

{
  "accessToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "tokenType": "Bearer",
  "expiresIn": 14400,
  "scope": "market:id:XYZ",
  "createdAt": 1747409730,
  "expires": "2025-05-16T19:35:30.245Z"
}

Storing the Token in LocalStorage

We can now improve our component and store the token in localStorage to reuse it across sessions. We’ll save the token after fetching it and we’ll also add helper functions to retrieve and validate it. First of all, we want to update the fetchAuthentication function and then to add some utility functions for working with the stored token.

const fetchAuthentication = async () => {
  const auth = await authenticate("client_credentials", {
    clientId,
    scope: `market:id:${market}`,
  });

  localStorage.setItem(`authentication-${market}`, JSON.stringify(auth));
	
  setAuthentication(auth);
};
const getStoredAuthentication = (market: string): Authentication | null => {
  const authAsString = localStorage.getItem(`authentication-${market}`);

  if (authAsString) {
    const localAuth = JSON.parse(authAsString);
    return {
      ...localAuth,
      expires: new Date(localAuth.expires),
    };
  }

  return null;
};

const hasExpired = (time?: Date): boolean => {
  if (!time) return true;
  return time < new Date();
};

const isValid = (auth: Authentication | null): auth is Authentication => {
  return auth !== null && !hasExpired(auth.expires);
};

Final Component

Now that we have all the pieces, we can combine them together and get the complete version of the CommerceLayerAuth component. It now handles token management, storage, and reuse. I’ve also added OrderStorage and OrderContainer to persist cart state for each market.

"use client";

import { ReactNode, useEffect, useState } from "react";
import { authenticate, AuthenticateReturn } from "@commercelayer/js-auth";
import {
  CommerceLayer,
  OrderContainer,
  OrderStorage,
} from "@commercelayer/react-components";

export type CommerceLayerAuthProps = {
  children: ReactNode;
  clientId: string;
  slug: string;
  market: string;
};

type Authentication = AuthenticateReturn<"client_credentials">;

const getStoredAuthentication = (market: string): Authentication | null => {
  const authAsString = localStorage.getItem(`authentication-${market}`);

  if (authAsString) {
    const localAuth = JSON.parse(authAsString);
    return {
      ...localAuth,
      expires: new Date(localAuth.expires),
    };
  }

  return null;
};

const hasExpired = (time?: Date): boolean => {
  if (!time) return true;
  return time < new Date();
};

const isValid = (auth: Authentication | null): auth is Authentication => {
  return auth !== null && !hasExpired(auth.expires);
};

const CommerceLayerAuth = ({
  children,
  clientId,
  slug,
  market,
}: CommerceLayerAuthProps) => {
  const [authentication, setAuthentication] = useState<Authentication | null>(
    null
  );

  useEffect(() => {
    const storedAuthentication = getStoredAuthentication(market);
    const storedAuthIsValid = isValid(storedAuthentication);

    if (storedAuthIsValid) {
      setAuthentication(storedAuthentication);
      return;
    }

    const fetchAuthentication = async () => {
      const auth = await authenticate("client_credentials", {
        clientId,
        scope: `market:id:${market}`,
      });

      localStorage.setItem(`authentication-${market}`, JSON.stringify(auth));
      setAuthentication(auth);
    };

    fetchAuthentication();
  }, [clientId, market]);

  if (!authentication) {
    return null;
  }

  return (
    <CommerceLayer
      accessToken={authentication.accessToken}
      endpoint={`https://${slug}.commercelayer.io`}
    >
      <OrderStorage persistKey={`order-${market}`}>
        {/* @ts-expect-error: OrderContainer children do not align with ReactNode type */}
        <OrderContainer>{children}</OrderContainer>
      </OrderStorage>
    </CommerceLayer>
  );
};

export default CommerceLayerAuth;

Wrap up

That’s it! We now have a reusable CommerceLayerAuth component that handles token management for our React apps. It retrieves, stores, and validates tokens per market, so you can focus on building your shopping experiences without worrying about authentication.