Navigating between pages (CSR)
Navigating between pages (CSR)
When building your commerce interfaces, it’s crucial to manage the state of the current URL using Headless. As users navigate to different pages, ensure that the URL is set and updated correctly to reflect the current view.
Additionally, you may want to manage URL parameters to sync them with the state of the interface. For example, when a user filters products on a search page or product listing page (PLP), you might want to update the URL to reflect the applied filters. This way, reloading the page or sharing the URL with others will display the same filtered results.
Manage page URL via the context.view object
Use Headless to send the URL information with every Commerce API request. This ensures that actions which affect the view, such as navigating to a new page, emit the correct analytics events.
It’s also important for product listing pages (PLPs) because you must specify the URL that corresponds to the PLP you want to target.
Setting URL on engine initialization
When initializing the commerce engine, you can set the URL by using the context.view object.
import { buildCommerceEngine } from '@coveo/headless/commerce';
import { loadCartItemsFromLocalStorage } from '../utils/cart-utils';
export const getEngine = () => {
if (_engine !== null) {
return _engine;
}
_engine = buildCommerceEngine({
configuration: {
organizationId: '<ORGANIZATION_ID>',
accessToken: '<ACCESS_TOKEN>',
analytics: {
trackingId: '<TRACKING_ID>'
},
context: {
currency: '<CURRENCY>',
country: '<COUNTRY>',
language: '<LANGUAGE>',
view: {
url: '<URL>'
},
}
cart: {
items: loadCartItemsFromLocalStorage() ?? [],
},
},
});
return _engine;
};
| Set the view URL to the current page URL. |
Modifying the URL on page change
When the user navigates to a new page, update the view URL using the Context controller.
import { engine } from './Engine';
import { buildContext } from '@coveo/headless/commerce';
const context = buildContext(engine);
onPageChange(newUrl: string) {
context.setView({ url: newUrl });
}
Initialize the Context controller by passing in the previously initialized engine. |
|
Call the setView method on the Context controller to change the url when the user navigates to a different page. |
For more details on how to set the URL in a sample project, see the sample in the Headless repository
|
|
For URL-based audiences defined in the Coveo Merchandising Hub (CMH) to function correctly with query parameters, you can pass the complete URL including query or hash parameters when calling
|
Syncing parameters with the URL
Additionally, you might need to sync search parameters with the URL. This ensures that filters, sorting, and other side effects, such as the current pagination, are reflected in the URL. As a result, users can reload the page or share it with others to see the same filtered results.
Headless for Commerce provides two sub-controllers to manage URL parameters:
-
urlManager: Automatically serializes parameters (such as query, sort criteria, and facet values) into a URL-ready string. This controller handles serialization and deserialization internally, making it easy to use and requiring minimal configuration.ExampleYour search page is configured with the query set to "hello" and results sorted by descending date. The
urlManagerserializes this state into the following string:q=hello&sortCriteria=date%20descending. -
parameterManager: Provides search parameters as an object rather than a string, offering full control over URL serialization and deserialization. This controller is useful when you must handle URL parameters in a specific way, but it requires a more complex setup.
|
|
Note
This article explains how to use the The primary difference is that, with the When using the commerce engine, replace the |
Using the urlManager sub-controller
import {
buildProductListing,
Cart,
CommerceEngine,
Context,
} from '@coveo/headless/commerce';
import { useEffect, useCallback } from 'react';
import SearchAndListingInterface from '../components/use-cases/search-and-listing-interface/search-and-listing-interface.js';
interface IProductListingPageProps {
engine: CommerceEngine;
contextController: Context;
url: string;
}
export default function ProductListingPage(props: IProductListingPageProps) {
const {engine, contextController, url} = props;
const productListingController = buildProductListing(engine);
const bindUrlManager = useCallback(() => {
const fragment = () => window.location.hash.slice(1);
const urlManager = productListingController.urlManager({
initialState: {fragment: fragment()},
});
const onHashChange = () => {
urlManager.synchronize(fragment());
};
window.addEventListener('hashchange', onHashChange);
const unsubscribeManager = urlManager.subscribe(() => {
const hash = `#${urlManager.state.fragment}`;
if (!productListingController.state.responseId) {
window.history.replaceState(null, document.title, hash);
return;
}
window.history.pushState(null, document.title, hash);
});
return () => {
window.removeEventListener('hashchange', onHashChange);
unsubscribeManager();
};
}, [productListingController]);
useEffect(() => {
contextController.setView({url});
const unsubscribe = bindUrlManager();
if (
!productListingController.state.isLoading &&
!productListingController.state.responseId
) {
productListingController.executeFirstRequest();
} else if (!productListingController.state.isLoading) {
productListingController.refresh();
}
return unsubscribe;
}, [contextController, url, productListingController, bindUrlManager]);
return (
<div className="ProductListingPage">
<SearchAndListingInterface searchOrListingController={productListingController}/>
</div>
);
}
Define a function that synchronizes the URL fragment with the urlManager state.
Use React’s useCallback hook to memoize the function between renders. |
|||
Create a function that extracts the hash fragment from the URL using the window object. |
|||
Initialize the urlManager sub-controller with the initial state of the fragment.
Depending on the product discovery solution you’re implementing, choose the appropriate |
|||
Add an event listener to the hashchange event to update the state of the fragment on the urlManager sub-controller. |
|||
Similarly, subscribe to the urlManager sub-controller to update the URL fragment in the browser using the History API.
Now, the state of the fragment in Headless will be synced with the URL fragment in the browser. |
|||
As discussed previously, when a user navigates to a new page, update the URL by calling the setView method on the Context controller.
Calling
|
|||
Call the bindUrlManager function to synchronize the URL fragment with the urlManager state. |
|
|
Note
In the example above, |
Updating language on page change
If you’re supporting multiple languages, you must first set the language when initializing the engine.
Then, when the user changes the language, update the state of the language using the setLanguage method on the Context controller.