Adding a New Page

Learn to create new pages (routes) in your Scandi app!

Suppose we wanted to create an "about us" page. Usually, we would do this by using the CMS system. However, for the sake of a simple example, suppose we want to hard-code this page in the codebase.

Creating a New Route

First, we need a React component responsible for rendering the page. These are called routes, and in Scandi, they can be found within the route directory.

While you could create the necessary boilerplate yourself, the scandipwa CLI utility can automate this for you. The create route command will create a new route. You can name yours anything you want, but we called it AboutUs:

scandipwa create route AboutUs

This should create a new subfolder under route and create all the necessary files inside. The command output will also tell us what happened:

NOTE!

     The following files have been created:
     src/route/AboutUs/AboutUs.component.js
     src/route/AboutUs/AboutUs.style.scss
     src/route/AboutUs/index.js

Let's create some basic placeholder content in the .component.js file:

    render() {
        return (
            <div block="AboutUs">
                <h1>Don&apos;t Panic</h1>
                <p>
                    Welcome to my website.
                </p>
            </div>
        );
    }

Now, we have created our route. But it doesn't appear anywhere in our app yet... It's just another component lying around, unused, in our codebase. Let's do something about it!

Registering the Route

In Scandi, the Router component is responsible for registering all routes. It uses react-router to specify which Routes should be visible in different URLs. Let's take a look at the original Router component. It contains a special field where it configures all of the routes available in the app:

    [SWITCH_ITEMS_TYPE] = [
        {
            component: <Route path={ withStoreRegex('/') } exact render={ (props) => <HomePage { ...props } /> } />,
            position: 10
        },
        {
            component: <Route path={ withStoreRegex('/cart') } exact render={ (props) => <CartPage { ...props } /> } />,
            position: 50
        },
        {
            component: <Route path={ withStoreRegex('/checkout/:step?') } render={ (props) => <Checkout { ...props } /> } />,
            position: 55
        },
	      // [...]
    ];

Above, we can see that the field [SWITCH_ITEMS_TYPE] is an array containing all of the routes in the app. Each of them has a component field containing a Route component. Each of these components has two important props – the path, which is the domain-relative URI of the route, as well as the render function itself. For more details, see react-router docs.

Now that we know where the routes are defined, we want to add our own to the list. In Scandi, all theme changes are made through the Override Mechanism – so let's override this Router!

If you wanted to do this manually, all you'd have to do is create a new component file in your theme src folder, matching the path of the original file. However, the scandipwa CLI utility can help us automate this. Type scandipwa override component Router to override the router component. When asked what to extend in the Router.component.js file, select the Router class and nothing else. Leave the answers blank in the other questions.

scandipwa override component Router
? Choose things to extend in Router.component.js Router
? What would you like to do with styles? Keep
? Choose things to extend in Router.config.js 
? Choose things to extend in Router.container.js 

NOTE!

     The following files have been created:
     src/component/Router/Router.component.js

Now the file is overridden, but, by default, it exports everything as in the original file. First, copy-paste the original [SWITCH_ITEMS_TYPE] field inside your overridden class. Then, you can add your own route:

    [SWITCH_ITEMS_TYPE] = [
        {
            component: <Route path={ withStoreRegex('/') } exact render={ (props) => <HomePage { ...props } /> } />,
            position: 10
        },
        {
            component: <Route path={ withStoreRegex('/about-us-page') } exact render={ (props) => <AboutUs { ...props } /> } />,
            position: 14
        },
	      // [...]

Because of the code style in the current original Scandi code, you will need to disable some ESLint rules for your code to compile:

/* eslint-disable react/jsx-no-bind */
/* eslint-disable @scandipwa/scandipwa-guidelines/no-jsx-variables */
/* eslint-disable max-len */
/* eslint-disable @scandipwa/scandipwa-guidelines/jsx-no-props-destruction */

Usually, you should avoid disabling ESLint rules unless necessary, but in this case, copying the original code style results in linting errors. In the future, we hope to improve code quality to avoid some of these rule violations.

We can now verify that the page is correctly registered and appears on the frontend:

While the page may work on the frontend, we still need to make changes to the backend code, so that it knows that /about-us-page is a valid URL. Currently, if you refresh /about-us-page and check the response status code from the server, you will see that it responds with a 404 Not Found code. While this may seem insignificant at first, it can actually hurt your SEO.

Backend Adjustments

How do we configure the Magento router to register the route on the backend as well? The ScandiPWA route717 module is responsible for resolving paths and determining if they are valid or not. If you add a new route, the route717 module needs to know about it so that the correct response code is returned from the backend.

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).

You can write your own custom validator if you need to – which you would have to do if you need to access the database to determine if the path is valid (for example, for products, you need to check if the product exists). However, in our case, all we need to do is add a simple AlwaysPass validator – because our about-us-page should always return a success status code, 200. First, create a new Magento module for this configuration. Then, it its di.xml file, add the following configuration:

<?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>

Make sure the new module is registered by running magento setup:upgrade. Now, when you refresh the page, you should see that your custom page returns a successful status code.

What's Next?

Congrats! You have now added a brand-new page to your Scandi app. As an exercise, you could try to add some more pages, perhaps with more complicated paths with variables (hint: see the react-router docs!). Or perhaps you want to add a few links that will lead to your new page? Use the Link component for this purpose. You can also play around with your new page, adding more styling and content if you want.

Last updated