--- title: Navigating between pages (CSR) slug: o7v87331 canonical_url: https://docs.coveo.com/en/o7v87331/ collection: coveo-for-commerce source_format: adoc --- # 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 synchronize them with the state of the interface. For example, when a user filters products on a search page or [product listing page (PLP)](https://docs.coveo.com/en/m1sf3187/), 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)](https://docs.coveo.com/en/m1sf3187/) 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](https://docs.coveo.com/en/o6r70022#initialize-the-headless-commerce-engine), you can set the URL by using the `context.view` object. ```ts import { buildCommerceEngine } from '@coveo/headless/commerce'; import { loadCartItemsFromLocalStorage } from '../utils/cart-utils'; export const getEngine = () => { if (_engine !== null) { return _engine; } _engine = buildCommerceEngine({ configuration: { organizationId: '', accessToken: '', analytics: { trackingId: '' }, context: { currency: '', country: '', language: '', view: { <1> url: '' }, } cart: { items: loadCartItemsFromLocalStorage() ?? [], }, }, }); return _engine; }; ``` <1> 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`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Context.html) controller. ```ts import { engine } from './Engine'; import { buildContext } from '@coveo/headless/commerce'; const context = buildContext(engine); <1> onPageChange(newUrl: string) { <2> context.setView({ url: newUrl }); } ``` <1> Initialize the `Context` controller by passing in the previously initialized engine. <2> 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](https://github.com/coveo/ui-kit/tree/master/packages/samples/headless-commerce-react/src/pages/home-page.tsx#L26) > **Warning** > > For URL-based audiences defined in the [Coveo Merchandising Hub (CMH)](https://docs.coveo.com/en/o5290573/) to function correctly with query parameters, you can pass the complete URL including query or hash parameters when calling `setView()`. > > ```ts import { engine } from './Engine'; import { buildContext } from '@coveo/headless/commerce'; const context = buildContext(engine); onPageChange(newUrl: string) { const currentUrl = new URL(newUrl, window.location.origin); currentUrl.search = window.location.search; context.setView({ url: currentUrl.href }); <1> } ``` > <1> Include query parameters to ensure that the Commerce API receives the complete URL for [audience](https://docs.coveo.com/en/2743/) targeting. ## 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](https://docs.coveo.com/en/o6r70022#interact-with-a-controller) to manage URL parameters: * [`urlManager`](#using-the-urlmanager-sub-controller): Automatically serializes parameters (such as query, sort criteria, and [facet](https://docs.coveo.com/en/198/) values) into a URL-ready string. This controller handles serialization and deserialization internally, making it easy to use and requiring minimal configuration. **Example** Your 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 must handle URL parameters in a specific way, but it requires a more complex setup. > **Note** > > This article explains how to use the `urlManager` sub-controller to manage URL parameters on a [product listing page (PLP)](https://docs.coveo.com/en/m1sf3187/). > Similar logic applies when using the `parameterManager` sub-controller. > > The primary difference is that, with the `parameterManager` sub-controller, you must manually serialize and deserialize parameters. > For details, see the [generic Headless example](https://docs.coveo.com/en/headless/latest/usage/synchronize-search-parameters-with-the-url#buildsearchparametermanager). > > When using the commerce engine, replace the `buildSearchParameterManager` with the corresponding `parameterManager` sub-controller from either the [`Search`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Search.html#parameterManager) controller or the [`ProductListing`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.ProductListing.html#parameterManager) controller. ### Using the `urlManager` sub-controller ```jsx 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(() => { <1> const fragment = () => window.location.hash.slice(1); <2> const urlManager = productListingController.urlManager({ <3> initialState: {fragment: fragment()}, }); const onHashChange = () => { urlManager.synchronize(fragment()); }; window.addEventListener('hashchange', onHashChange); <4> const unsubscribeManager = urlManager.subscribe(() => { <5> 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}); <6> const unsubscribe = bindUrlManager(); <7> if ( !productListingController.state.isLoading && !productListingController.state.responseId ) { productListingController.executeFirstRequest(); } else if (!productListingController.state.isLoading) { productListingController.refresh(); } return unsubscribe; }, [contextController, url, productListingController, bindUrlManager]); return (
); } ``` <1> Define a function that synchronizes the URL fragment with the `urlManager` state. Use React's [`useCallback`](https://react.dev/reference/react/useCallback) hook to memoize the function between renders. <2> Create a function that extracts the hash fragment from the URL using the `window` object. <3> Initialize the `urlManager` sub-controller with the initial state of the fragment. Depending on the [product discovery solution](https://docs.coveo.com/en/o9cf0524/) you're implementing, choose the appropriate `urlManager` sub-controller: [`Search`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Search.html#urlManager) or [`ProductListing`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.ProductListing.html#urlManager). <4> Add an event listener to the `hashchange` event to update the state of the fragment on the `urlManager` sub-controller. <5> Similarly, subscribe to the `urlManager` sub-controller to update the URL fragment in the browser using the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API). Now, the state of the fragment in Headless will be synchronized with the URL fragment in the browser. <6> [As discussed previously](#manage-page-url-via-the-context-view-object), when a user navigates to a new page, update the URL by calling the `setView` method on the `Context` controller. Calling `setView` resets the state of [facets](https://docs.coveo.com/en/198/), sorting, and pagination. This behavior is necessary because the user may have navigated to a different page where these settings are no longer applicable. However, if the user hasn't navigated to a new page, such as when loading a page for the first time or reloading the same page, it's important to restore the parameters encoded in the URL fragment. Thus, `setView` should be called before calling `bindUrlManager()`. > **Important** > > If you use URL-based [audiences](https://docs.coveo.com/en/2743/), make sure that the `url` parameter you pass to `setView` includes the query or hash parameters, as shown in the [Modifying the URL on page change](#modifying-the-url-on-page-change) section. <7> Call the `bindUrlManager` function to synchronize the URL fragment with the `urlManager` state. > **Note** > > In the example above, `setView` is called before `bindUrlManager` to ensure the user's intended state isn't lost when the page is loaded or reloaded. > Similar logic applies when you're using other [methods to update the `Context` controller](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Context.html). ## Updating language on page change If you're supporting multiple languages, you must first set the language when [initializing the engine](https://docs.coveo.com/en/o6r70022#initialize-the-headless-commerce-engine). Then, when the user changes the language, update the state of the language using the [`setLanguage`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Context.html#setLanguage) method on the `Context` controller.