Routing

Learn how to navigate between different areas of your customizations.

When building a customization, you might encounter a use case where you need to navigate between different areas. For example, if you have a list of items and you want to navigate to a detail view when the user clicks on one of the items.

To facilitate the use of routing, customizations have built-in support for routes and client-side navigation using the react-router-dom library, an abstraction on top of the History API.

Routing system

Both Custom Applications and Custom Views have a pre-configured router in their shell components. The router determines the baseline and the capabilities of all possible routes within the customizations:

Merchant Center customizations use a browser router that is responsible for client-side navigation.

The baseline routes are defined as following:

  • Custom Applications: /:projectKey/:entryPointUriPath
  • Custom Views: /custom-views/:customViewId/projects/:projectKey

The routing within a Custom View is decoupled from the host application for security purposes because the Custom View is loaded in a sandboxed iframe.

Every new route added to your customization is considered relative to the baseline route.

For example, if you are implementing a Custom Application to manage Channels you may have a route for a details page: /:channelId. The full route would then be /:projectKey/:entryPointUriPath/:channelId.

Types of route paths:

  • Template path: the path assigned to a route used to match a location.
  • Real path: the real location path to navigate to.

When defining a route, the template path must be used. For example, /:projectKey/:entryPointUriPath/:channelId. When navigating to another location, the real path must be used. For example, /my-project/custom-channels/123.

The react-router-dom library provides additional features to make it simpler to define relative and nested routes. For instance, it is not necessary to type the full path (including the baseline). Instead, we can rely on the context where the route is defined and use the route match to build the nested routes. This can be done by using the useRouteMatch hook of react-router-dom.

trueTypeScript React
import { useRouteMatch } from 'react-router-dom';
import ChannelDetails from './channel-details';
const Channels = () => {
const match = useRouteMatch();
return (
<Route path={`${match.path}/:channelId`}>
<ChannelDetails />
</Route>
);
};

Routes abstraction

The @commercetools-frontend/application-shell package provides a useRoutesCreator hook that can be used to abstract some of the routing setup as well as to improve the developer experience when working with routes.

With this abstraction, you can define all possible routes in one place and configure them based on the routes requirements. This includes defining the required route parameters.

Create a file use-routes.ts, which is a React hook that returns the list of all possible routes.

use-routes.tsTypeScript
import { useRoutesCreator } from '@commercetools-frontend/application-shell';
import { entryPointUriPath } from './constants';
const useRoutes = () => {
const { createRoute } = useRoutesCreator();
const routes = {
main: createRoute(`/:projectKey/${entryPointUriPath}`),
channelDetails: createRoute<'channelId'>(
`/:projectKey/${entryPointUriPath}/:channelId`
),
};
return routes;
};
export default useRoutes;

Each configured route is an object with the following properties:

  • path: the value of the template path for the given route.
  • getUrl: a function that returns the real path for navigating to the given route. If the path includes parameters (for example, channelId), they must be provided as named arguments to the function.
  • go: a function that triggers a navigation to the given route. If the path includes parameters (for example, channelId), they must be provided as named arguments to the function.
Example of using the useRoutes hookTypeScript React
import useRoutes from './use-routes';
import ChannelDetails from './channel-details';
const Channels = () => {
const routes = useRoutes();
return (
<Route path={routes.channelDetails.path}>
<ChannelDetails />
</Route>
);
};

The parameters projectKey and entryPointUriPath are automatically assigned by the routes creator and do not need to be explicitly provided as route parameters.

When navigating to a route, a second function argument can be provided to pass query parameters. The expected argument must be an instance of URLSearchParams.

routes.channelDetails.getUrl(
{ channelId: '123' },
new URLSearchParams({ isNew: 'true' })
);
// Returns: /my-project/custom-channels/123?isNew=true

Example routes

The following example defines two routes. For that, we need to specify a Switch component from react-router-dom that is responsible for matching and rendering the correct route.

import { Route, Switch, useRouteMatch } from 'react-router-dom';
import Channels from './components/channels';
import ChannelDetails from './components/channel-details';
const ApplicationRoutes = () => {
const match = useRouteMatch();
return (
<Switch>
<Route path={`${match.path}/:channelId`}>
<ChannelDetails />
</Route>
<Route>
<Channels />
</Route>
</Switch>
);
};

Use the Link component to navigate between pages.

import { useRouteMatch } from 'react-router-dom';
import Link from '@commercetools-uikit/link';
const Channels = () => {
const match = useRouteMatch();
return (
<div>
<h1>Welcome to the Channels application</h1>
<Link to={`${match.url}/123`}>Go to Channel 123</Link>
<Link to={`${match.url}/456`}>Go to Channel 456</Link>
</div>
);
};