To implement a new Route you need to define what component you want to render and in which path. For example, You want to render the component <YourPage> in the path /yourPage/:query/.

<aside> ✅ It’s a convention to keep the components used in the Router in the src/route directory. If you don’t have a component, consider creating one using the ScandiPWA CLI tool. How to create a Route component?

scandipwa create route <NAME>

This will create the route’s component files under src/route/<NAME> folder, it will give you some boilerplate code. It is up to you to render the required interface and add functionality.

</aside>

After defining what component and in which path, you need to:

  1. **Register your component in the Router** - It’s necessary that the Router component knows the component you want to render and in which path. For that, we make use of the plugin mechanism.
  2. Register the path in Magento 2 to return the correct status code - You need to ensure that the Magento backend knows about the new route, this is necessary for SEO reasons. This step depends on whether your path is static or dynamic.

How to register your component in the Router?

You now have a component, but it's not registered in the router yet. You need to add it to the SWITCH_ITEMS_TYPE field of the router.

Write a plugin (How to create a plugin?) for the Component/Router/Component namespace to wrap around the SWITCH_ITEMS_TYPE field and add your route there.

Most likely, your plugin will look similar to this one:

import { lazy } from 'react';
import { Route } from 'react-router-dom';

import { withStoreRegex } from 'Component/Router/Router.component';

export const YourPage = lazy(() => import(
    /* webpackMode: "lazy", webpackChunkName: "extension-name" */
    '../route/YourPage'
    ));
export const ROUTE_NAME = 'SOME_ROUTE_NAME';

const SWITCH_ITEMS_TYPE = (originalMember) => [
    ...originalMember,
    {
        component: <Route 
            path={ withStoreRegex('/yourPage/:query/') } 
            render={ (props) => <YourPage { ...props } /> } 
        />,
        position: 25,
        name: ROUTE_NAME
    },
];

export default {
    'Component/Router/Component': {
        'member-property': {
            SWITCH_ITEMS_TYPE
        }
    }
};

Make sure to use this import instead of the conventional import ... from ... syntax:

lazy(() => import(/* webpackMode: "lazy", webpackChunkName: "cms" */ 'Route/CmsPage')); 

The lazy import syntax helps improve the initial load performance, because the browser only needs to render the currently active page, so loading the code for other routes can be postponed.

What are the configuration options?

You will notice that each route has quite a few parameters you can change:

{
    component: <Route
        path={ withStoreRegex('/search/:query/') }
        render={ (props) => <SearchPage { ...props } /> }
    />,
    position: 25,
},

How to return the correct status code?

<aside> ⚠️ For development-purpose pages like /base-template, this step is optional.

</aside>

Once you have added a new route on the frontend, you need to ensure that the Magento backend knows about it too.

If you are using ScandiPWA in Magento theme mode and refresh the page you just created, check the response status code from the server in the developer tools. You will see that it responds with a 404 Not Found code. While this may seem insignificant at first, it can actually harm your SEO.

It would be much better to have the correct status codes: 404 for non-existent pages and a 200 Ok response for valid URLs.

Looking at scandipwa/route717/src/etc/di.xml, we can see an example of how the routes can be configured:

<?xml version="1.0"?>
<config xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- [...] -->
    <type name="ScandiPWA\\Router\\ValidationManager">
        <arguments>
            <argument name="validators" xsi:type="array">
                <item name="cart" xsi:type="string">ScandiPWA\\Router\\Validator\\AlwaysPass</item>
                <item name="wishlist" xsi:type="string">ScandiPWA\\Router\\Validator\\Wishlist</item>
                <item name="checkout" xsi:type="string">ScandiPWA\\Router\\Validator\\AlwaysPass</item>
            </argument>
        </arguments>
    </type>
    <!-- [...] -->
</config>

The validators argument of the ValidationManager is an array mapping paths to corresponding validators. The validators' job is to determine if a path is valid (200 status code) or invalid (404 not found).

The ScandiPWA\\Router\\Validator\\AlwaysPass validator will always return 200. This is useful for "static" routes without variables.

However, for more complex cases, you will have variables inside the route. For example, a product route will be 200 for existing products but 404 for other URLs.

What are response status codes?

How you configure the router will depend on if your route is static (always the same) or dynamic (with a variable in the path). If your route does not include any variables, you can simply tell the backend router that it should always return a 200 Ok status code. If, however, you need custom logic with variables in the route, you will need to implement your own validator.

If your route does not include any variables, you can simply tell the backend router that it should always return a 200 Ok status code.

First, create a new Magento module for this configuration. Then, in its di.xml file, add an entry for the validators argument of ScandiPWA\\Router\\ValidationManager:

<?xml version="1.0"?>
<config xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="ScandiPWA\\Router\\ValidationManager">
        <arguments>
            <argument name="validators" xsi:type="array">
                <item name="about-us-page" xsi:type="string">ScandiPWA\\Router\\Validator\\AlwaysPass</item>
            </argument>
        </arguments>
    </type>
</config>

Sometimes, whether a route is valid or not will depend on a dynamic variable. For example, the /product route is valid for existing products like /product/black_hoodie but invalid for non-existing URLs like /product/nonexistent-product.

If that is the case, you need to create a new validator class by implementing the \\ScandiPWA\\Router\\ValidatorInterface:

<?php
namespace ScandiPWA\\Router\\Validator;

use Magento\\Framework\\App\\RequestInterface;
use ScandiPWA\\Router\\ValidatorInterface;

class CustomValidator implements ValidatorInterface
{
    public function validateRequest(RequestInterface $request): bool
    {
        // custom validation logic
    }
}

<aside> ℹ️ The request object is of type Magento\\Framework\\App\\Request\\Http

</aside>

You can then configure the DI system to inject your validator into the ScandiPWA\\Router\\ValidationManager object.

SWITCH_ITEMS_TYPE