Using the frontend SDK
Learn about using various features of the Frontend SDK.
Getting started
Install the SDK
The presence of the SDK, its integrations, and dependencies in commercetools Frontend projects may vary based on when the project was created and whether it's tailored for B2C or B2B Composable Commerce editions. Typically, these components should already be installed, configured, and ready for use right out of the box.
To confirm the SDK's setup within your project repository, inspect the sdk
directory located at the packages/PROJECT_NAME/frontend/sdk/
path. The presence of this directory indicates that your project includes the SDK. By default, this folder should contain the following files:
index.ts
CommercetoolsSDK.ts
templatecomposable-commerce
folder for B2C projects or acomposable-commerce-b2b
folder for B2B projects
If the directory is not present in your commercetools Frontend project, follow these steps:
Install the base SDK NPM package in your commercetools Frontend project by running
yarn add @commercetools/frontend-sdk
in the project directory.
We recommend installing the latest version of this package to ensure you get all features and full compatibility with your integrations. This package is always backward compatible.The Composable Commerce integration is distributed as editable code. Clone or download the code for B2C projects from the frontend-composable-commerce repository and for B2B projects from the frontend-composable-commerce-b2b repository.
From the downloaded folder, copy the contents of the
src
folder into your project atpackages/PROJECT_NAME/frontend/sdk/composable-commerce/
for B2C projects orpackages/PROJECT_NAME/frontend/sdk/composable-commerce-b2b/
for B2B projects.Check the dependencies in the
package.json
file of the SDK integration to ensure compatibility and install any missing packages.Add the following files to your Frontend project's
sdk
folder atpackages/PROJECT_NAME/frontend/sdk/
.An
index.ts
file that exports thesdk
instance. This file is for convention and ease of import throughout the project. All custom events that you create must also be exported from this file.packages/PROJECT_NAME/frontend/sdk/index.tsTypeScriptimport { sdk } from './CommercetoolsSDK';export { sdk };A
CommercetoolsSDK.ts
template file.
To ensure all event handlers are stored and triggered correctly, all parts of the SDK must operate as singletons to prevent duplication of events on construction, including integrations, the base SDK, and the template file.
The
CommercetoolsSDK.ts
template file creates and exports only an instance of the extended class to ensure its own singleton behavior. This file is the single source of truth for anything related to the SDK and its integrations.Import and configure the SDK in an index location within your project. This needs to be called only once because the SDK is exported as a singleton. However, the SDK must be configured for both client-side and server-side rendering.
The following examples import and configure the SDK in a Next.js 12 project.
For client-side configuration, add the below to your
packages/PROJECT_NAME/frontend/pages/_app.tsx
file.packages/PROJECT_NAME/frontend/pages/_app.tsxTypeScript Reactimport { AppProps } from 'next/app';import { useRouter } from 'next/router';import { sdk } from '../sdk';const Starter = ({ Component, pageProps }: AppProps) => {const router = useRouter();sdk.defaultConfigure(router.locale as string);return <Component {...pageProps} />;};export default Starter;For server-side configuration, add the below to your
packages/PROJECT_NAME/frontend/pages/[[...slug]].tsx
file in thegetServerSideProps
function.packages/PROJECT_NAME/frontend/pages/[[...slug]].tsxTypeScript Reactimport { GetServerSideProps, Redirect } from 'next';import { sdk } from '../sdk';......export const getServerSideProps: GetServerSideProps | Redirect = async ({params, locale, query, req, res}) => {sdk.defaultConfigure(locale as string);...};In Next.js 13, the client-side index location is
packages/PROJECT_NAME/frontend/src/app/[locale]/[[...slug]]/page.tsx
and server-side is in any of the API routes or server components.
The SDK and Composable Commerce integration is set up now.
Configure the SDK
The configure
method is defined on the base SDK main class in the @commercetools/frontend-sdk
library and has several optional and required properties, the required properties will already be set up in your project using the defaultConfigure
method in the CommercetoolsSDK.ts
template file. The configure
method supports the following options:
locale
- String - Required. The combination of the language and country code in ISO 639-1 and ISO 3166-1 format respectively. For example,en-DE
oren_DE
. In your code, you can access thelocale
from thePageProps.params.locale
(Next.js 12) or using theuseParams()
hook (Next.js 13).currency
- String - Required. The three-letter ISO 4217 Currency Code. For example,EUR
. For more information, see supported currencies.endpoint
- String - Required. The full URL of the API hub endpoint.extensionVersion
- String - Required. The extension bundle version to connect to.useCurrencyInLocale
- Boolean - Optional. Iftrue
, the currency is required in thelocale
in the formatLOCALE@CURRENCY
. Defaults tofalse
. Overrides thecurrency
option.sessionLifetime
- Number - Optional. This is the amount of time in milliseconds for which a user's session persists before needing to log in again. Overrides the default session lifetime of three months.customHeaderValue
- String - Optional. This is the value sent as thecoFE-Custom-Configuration
header value with every request. You can override this value on a specific request by setting thecustomHeaderValue
option in the API methods. You can access this value globally using theSDK.customHeaderValue
property.cookieHandlingOverride
- CookieManager - Optional. This option gives the user the ability to extend or override the default base SDK's CookieHandler.
The following code example shows how to extend the default cookie handling using thedefaultConfigure
method in the SDK template file.Example code to specify custom cookie handling logicTypeScriptimport { sdk } from 'src/sdk';import { CookieHandler, ServerOptions } from '@commercetools/frontend-sdk';const cookieHandler = new CookieHandler();sdk.configure({...cookieHandlingOverride: {setCookie: async (key: string, data: any, options?: ServerOptions) => {cookieHandler.setCookie(key, data, options);},getCookie: async (key: string, options?: ServerOptions) => {return cookieHandler.getCookie(key, options);},getCookies: async (options?: ServerOptions) => {return cookieHandler.getCookies(options);},deleteCookie: async (key: string, options?: ServerOptions) => {return cookieHandler.deleteCookie(key, options);},hasCookie: async (key: string, options?: ServerOptions) => {return cookieHandler.hasCookie(key, options);},},});
API methods
The base SDK provides various methods to interact with the backend extensions API. We recommend using the SDK methods for backend extension requests because the base SDK lets you configure options such as the locale, currency, endpoint, and extension version, and it maintains these throughout your application.
All API methods return the SDKResponse type, which has an isError
boolean property that you can use for narrowing. When isError
is false,
the response contains a data
property with the return type from the API hub. When true
, the response contains an error
property that includes the error details.
callAction
The callAction
method lets you make requests to the action extensions. The method takes the expected return type as it's generic argument that defines the type of data
returned in the SDKResponse of a successful request. It also accepts the following options:
actionName
- String - Required. The name of the action extension to call. For example, useproduct/getProduct
to call the following extension:{ actions: { product: { getProduct: (...) => { ... } } } }
.payload
- Object - Optional. A payload object with key-value pairs to be serialized into the request body.query
- Object - Optional. An object of key-value pairs to be serialized into the request query parameters. It accepts the value types specified in AcceptedQueryTypes.skipQueue
- Boolean - Optional. Defaults tofalse
. Set totrue
to skip the action queue and execute fully asynchronously. May cause race conditions if used incorrectly.customHeaderValue
- String - Optional. The value to assign to thecoFE-Custom-Configuration
header value. Overrides the globalcustomHeaderValue
option set when configuring the SDK.serverOptions
- ServerOptions - Optional for client-side configuration and required for server-side session management. Contains thereq
object of type IncomingMessage andres
object of type ServerResponse with cookies.
By default all actions are added to a queue and executed synchronously to avoid potential race conditions. You can change the default behavior by using the skipQueue
option.
The following code example uses the callAction
method to call a custom extension customActions/getCustomerCoupons
with a custom header, query, and payload.
You only need to use the callAction
method for custom actions. For commercetools extensions, you can use the sdk.composableCommerce
integration to access the extensions.
const response = await sdk.callAction<Customer>({actionName: 'customActions/getCustomerCoupons',payload: { customer: { email: 'username@example.com' } },query: { customerId: '4' },skipQueue: true,customHeaderValue: '{"customerAuthId":9188377992}',});if (response.isError) {setError(response.error);} else {setCustomer(response.data);}
getPage
The getPage
method retrieves the page data for static and dynamic pages from the API hub. This method is primarily used fetch the page data from the Studio and render the pages with the Frontend components. This method is used at the catch-all route packages/<projectName>/frontend/app/[locale]/[[...slug]]/page.tsx
. This method accepts the following options:
path
- String - Required. The relative path of the page of which you want to fetch the data. For example,/sale
or/home/best-sellers
.query
- Object - Optional. An object of key-value pairs to be serialized into the URL query parameters. It accepts the value types specified in AcceptedQueryTypes.customHeaderValue
- String - Optional. The value to assign to thecoFE-Custom-Configuration
header value. Overrides the globalcustomHeaderValue
option set when configuring the SDK.serverOptions
- ServerOptions - Optional for client-side configuration and required for server-side session management. Contains thereq
object of type IncomingMessage andres
object of type ServerResponse with cookies.
The following code example uses the getPage
method to get the /sale
page information with a query and custom header:
const response = await sdk.page.getPage({path: '/sale',query: { size: 'M' },customHeaderValue: '{"customerAuthId":9188377992}',});if (response.isError) {const router = useRouter();router.push('/404');} else {setPageData(response.data);}
getPreview
The getPreview
method retrieves the preview data for Studio page previews, used at packages/<projectName>/frontend/app/[locale]/preview/[previewId]/page.tsx
. This method accepts the following options:
previewId
- String - Required. A string representing the ID of the preview to fetch.customHeaderValue
- String - Optional. The value to assign to thecoFE-Custom-Configuration
header value. Overrides the globalcustomHeaderValue
option set when configuring the SDK.serverOptions
- ServerOptions - Optional for client-side configuration and required for server-side session management. Contains thereq
object of type IncomingMessage andres
object of type ServerResponse with cookies.
The following code example uses the getPreview
method to get page preview data with the previewId
and custom header:
const response = await sdk.page.getPreview({previewId: 'p9986b2d', // Replace this with the variable containing the previewIdcustomHeaderValue: '{"customerAuthId":9188377992}',});if (response.isError) {handleError(response.error);} else {setPreviewData(response.data);}
getPages
The getPages
method lets you fetch the page data for a page folder and all it's sub-pages. This method is primarily used in B2C projects to generate the sitemap for static pages at packages/<projectName>/frontend/app/[locale]/sitemap-static.xml/route.ts
. This method accepts the following options:
path
- String - Optional. Defaults to/
. The relative path of the page of which you want to fetch the data.depth
- Number - Optional. Defaults to16
. The depth of the page folder tree up to which you want to fetch the data.types
- String - Optional. Defaults tostatic
. The types of pages to fetch.customHeaderValue
- String - Optional. Defaults to an empty string. The value to assign to thecoFE-Custom-Configuration
header value. Overrides the globalcustomHeaderValue
option set when configuring the SDK.serverOptions
- ServerOptions - Optional for client-side configuration and required for server-side session management. Contains thereq
object of type IncomingMessage andres
object of type ServerResponse with cookies.
The following code example uses the getPages
method to get the page data for all pages under the /sale
hierarchy up to two levels deep. For example, /sale
, /sale/shirts
, /sale/shirts/special
, and so on.
const response = await sdk.page.getPages({path: '/sale',depth: 2,customHeaderValue: '{"customerAuthId":9188377992}',});if (response.isError) {handleError(response.error);} else {generateSitemap(response.data.pageFolderStructure);}
Add SDK integrations
It is possible to extend the SDK with the integrations you develop. To do so, set up the integrations in the CommercetoolsSDK
constructor and pass the SDK instance, such as the ComposableCommerce
instance. For example:
//.... Other codeconstructor() {super();// customIntegration is an example name herethis.customIntegration = new CustomIntegration(this);}//.... Other code
Additionally, any custom events must be added to the SDK generic type as a type intersection. For example:
class CommercetoolsSDK extends SDK<ComposableCommerceEvents & CustomIntegrationEvents> {...}
The backend actions must be added to your backend service to extend your extensions API.
The event engine
The commercetools Frontend SDK comes with event management tools to let integrations communicate with other integrations and the user of the integration to add or create an event handler. The source for this functionality is the EventManager
class, extended by the SDK. It is also possible to extend the event types with custom events with the generic argument passed from the SDK.
Following is a description of the three methods available on the SDK to manage event handlers:
trigger
is called to trigger an event, for which an instance of theEvent
class from@comercetools/frontend-sdk
is passed.
An event is constructed witheventName
anddata
. TheeventName
corresponds to the[key: string]
value in@comercetools/frontend-sdk
'sStandardEvents
. In custom eventsdata
corresponds to the type of value set for the event.
For example, to trigger themarketingBannerClicked
custom event,trigger
is called on thesdk
and an event constructed witheventName: 'marketingBannerClicked'
anddata: { id: "<string>" }
is passed.on
is called to add an event handler for an event. The method takes theeventName
andhandler
arguments.
For example, for themarketingBannerClicked
custom event,marketingBannerClicked
is passed for theeventName
argument and a function withevent
parameter of type{ data: { id: "<string>" } }
is passed for thehandler
argument.off
is called to remove an event handler. For example, to persist the handler only for the lifecycle of a particular component.
The function takes the same arguments as theon
function. For it to work, a named function for thehandler
argument must be defined. To successfully pass a named function to thehandler
parameter, theevent
type in the function's argument must be fully typed, as shown in the following examples.
Events are likely to be triggered only during action calls. The integrations you use may also create handlers so they can communicate with other integrations. However, you must be careful to avoid infinite recursion by mistakingly triggering events from event handlers.
The SDK event engine should only be used for events specific to the base SDK and integrations. The standard React events should be used for component events such as onclick
and onchange
.
Create custom events handlers
The commercetools Frontend SDK lets you create custom events that you can trigger from within the application, such as when clicking on a particular component or holding the pointer over it.
By default, event triggers and handlers will exist for the website's lifetime. However, you may want some to exist only during the lifetime of a specific React component. In that case, you must remove the event handlers on component unmounts. Otherwise, events can stack up due to component unmounting and remounting and may attempt to perform state updates on unmounted components, depending on the nature of the custom events.
To understand event handlers better, lets suppose you have a MarketingBanner
component and you want to track how many times the banner image is clicked using the marketingBannerClicked
event. To achieve this behavior, the MarketingBanner
component can use the onClick
React event handler, which triggers the marketingBannerClicked
event.
To add custom event triggers to the SDK event definition, follow these steps:
Create a
MyCustomEvents.ts
file in thepackages/PROJECT_NAME/frontend/sdk
folder with the following content:Custom events type definitionTypeScript// packages/PROJECT_NAME/frontend/sdk/MyCustomEvents.tsexport type MyCustomEvents = {marketingBannerClicked: { id: string };};Add the type you created to the
CommercetoolsSDK
class in the generic SDK argument. The type must be added in the form of an intersection, as shown in the following example:Extend the base SDK type with custom events typeTypeScript// packages/PROJECT_NAME/frontend/sdk/CommercetoolsSDK.tsimport { SDK } from "@commercetools/frontend-sdk";import {ComposableCommerce,ComposableCommerceEvents} from "@commercetools/frontend-composable-commerce";import { MyCustomEvents } from "./MyCustomEvents";class CommercetoolsSDK extends SDK<ComposableCommerceEvents & MyCustomEvents> {...}...Implement the
MarketingBanner
React component. In this example, we're making a simple banner component with an image.MarketingBanner React component implementation with event handlersTypeScript Reactimport Image from 'next/image';import { useEffect } from 'react';import { sdk } from '../../sdk';import { Event } from '@commercetools/frontend-sdk';interface Props {marketingId: string;imageSrc: string;}const MarketingBanner = ({ marketingId, imageSrc }: Props) => {const marketingBannerClickedHandler = (event: Event<'marketingBannerClicked',{id: string;}>) => {// Perform custom event handling logic here.console.log('Marketing banner clicked, id: ' + event.data.id);};const clickHandler = (id: string) => {sdk.trigger(new Event({eventName: 'marketingBannerClicked',data: {id: id,},}));};useEffect(() => {sdk.on('marketingBannerClicked', marketingBannerClickedHandler);return () => {sdk.off('marketingBannerClicked', marketingBannerClickedHandler);};}, []);return <Image src={imageSrc} onClick={() => clickHandler(marketingId)} />;};export default MarketingBanner;We define a function named
marketingBannerClickedHandler
that takes a parameterevent
of classEvent
from the@commercetools/frontend-sdk
library. This class takes theEventName
andEventData
parameters as the generic arguments, matching the event we defined earlier in theMyCustomEvents.ts
file by name (key) and data (value) respectively.Then, we define a function
clickHandler
that constructs themarketingBannerClicked
event and triggers it using thesdk.trigger
method. The event has the following properties:eventName
: the name of the custom event.data
: value of the custom event whereid
is the identifier of the clicked marketing banner.
Then, we use the React
useEffect
hook is to callsdk.on
to set up the event handler on component mount andsdk.off
to remove the handler on component unmount by passing the named handler on both occasions.Finally we call the
clickHandler
from theonClick
event of theImage
component.
For more information about how you can extend the SDK with integrations, see Developing SDK integrations.