Navigating between pages
Navigating between pages
This is for:
DeveloperWhen building your commerce interfaces, it’s crucial to manage the state of the current URL using Headless. As users navigate to different pages, you need to ensure that the URL is set and updated correctly to reflect the current view.
Additionally, you may want to manage URL parameters to synchronize them with the state of the interface. For example, when a user filters products on a search 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, ensuring that actions affecting the view—such as navigating to a new page—emit the correct analytics events.
This is also important for PLPs because you need to specify the URL that corresponds to the PLP configuration 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, you must 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
Synchronizing parameters with the URL
Additionally, you might need to synchronize 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 (query, sort criteria, facet values etc) 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
urlManager
serializes 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 need to 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 on the browser using the History API.
Now, the state of the fragment in Headless will be synchronized 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.