Rendering and Routing

The PWA Kit architecture builds on React’s rendering system, adding features that benefit both users and developers. This architecture guide takes a closer look at these features of PWA Kit’s rendering and routing system and how they work. Special attention is given to the parts of the PWA Kit React SDK and the Retail React App that come together to power each feature.

The following table lists each major feature of the rendering and routing system with a summary of its benefits.

Server-side renderingSpeeds up initial page load, especially for cached requests.
Client-side renderingSpeeds up subsequent page load and enables a fluid user experience.
Request routingSimplifies how to handle requests for pages and data by mapping request patterns to specific components.
Special componentsLet you configure and customize your storefront across multiple pages in your React app.

Server-Side Rendering

To create a favorable first impression, the first page that a user requests from your storefront must be loaded as fast as possible. But first page requests demand more rendering time than subsequent page requests. Every component on the page must be rendered, not just the ones that need updating. For the critical first page load, we use server-side rendering because it offers a powerful tool for optimizing performance: caching.

The Managed Runtime’s CDN cache can store a previously rendered version of a page and serve it to the user in an instant. There’s no faster render than one that’s already done!

To learn how to boost performance through caching, see our guide to Maximizing Your Cache Hit Ratio.

The App Server

The main code for server-side rendering is run inside the app server. The app server is an Express app that handles incoming HTTP requests and calls a function called render in response. The app server is defined in app/ssr.js.

The render function is imported from the PWA Kit React SDK. Helper functions (also from the SDK) create an instance of Express and attach the render function to it as an Express route method for all GET requests.

Client-Side Rendering

After the first page load, rendering duties are transferred from the server side to the client side through a process called hydration. During hydration, your React app starts running in the user’s browser. Event handlers spring into action to respond to user input. Page elements are rendered as needed and as directed by the user input in client-side rendering. This more efficient and user directed rendering enables the fluid experience that React is famous for.

The PWA Kit architecture works behind the scenes to make the transition from server-side rendering to client-side rendering as smooth as possible. For example, all your components are preloaded with all the props they need, even props that were the result of API requests. During server-side rendering, these props are serialized and embedded into the page source so that they can be used during hydration.

Isomorphic Code

Because the same code is rendered in both server-side and client-side environments, it must be isomorphic. Isomorphic JavaScript (also known as universal JavaScript) is code that doesn’t rely on direct access to any code construct that is only available in one runtime environment or another. When a construct isn’t available in both environments, you must add conditional code around it to make sure that it’s available in the current environment.

The following table lists some commonly used constructs that must be used with conditional code:

window.locationCurrent URL from the browserClient-side only
reqHTTP request from ExpressServer-side only
resHTTP response from ExpressServer-side only

To determine whether rendering is happening on the client side or the server side, check for the presence of the window object. The following example uses this technique to render a price only on the client side:

Some content, such as personalized or frequently changing content, must only be rendered on the client side to get the best possible performance. See our guide to Maximizing Your Cache Hit Ratio for more information.

Request Routing

Now that you know how a PWA Kit storefront handles rendering, let’s consider how it knows what to render in the first place.

When a user makes a GET request on your storefront domain, your React app chooses one component to render in response. This type of component is called a route. All the routes available for rendering are defined in an array of objects called routes in app/routes.jsx.

The routes array follows the route configuration shape, as defined by React Router. Each object in the routes array can have the following properties:

KeyTypeDescriptionReference Docs
pathAn Express-style string, an array of strings, or a regular expressionThe path that is compared to the incoming request path for a potential matchReact Router API → <Route> → path
componentFunction (imported into routes.jsx)Component to render if the path matches the requestReact Router API → <Route> → component
exactBooleanDetermines whether the path must be an exact match or notReact Router API → <Route> → exact

A newly generated project already includes objects in the routes array for many standard ecommerce pages, such as home, PLP, and PDP.

React Router

But how does your React app choose the right component to render for any given request? We use the React Router library to search sequentially through the route configuration objects in the routes array until it finds a path string that matches the requested path.

React Router gives you lots of options for constructing your path strings. You can specify more than one path for the same component and use regular expressions to match paths that follow a pattern.

React Router is also used throughout the Retail React App to implement navigation. For example, all hyperlinks use React Router’s Link component. React Router offers other components that give you access to browser history, query parameters, and more.

To learn more about using React Router, see the official documentation. (Stick to the documentation for version 5 because older versions use a different pattern matching system.)

The routeComponent Function

Each component specified in the routes array is automatically enhanced by the routeComponent function, a higher-order component from the PWA Kit React SDK. The base class that is used to construct routeComponent defines several static methods, including two important ones that storefront developers can customize: getProps and shouldGetProps.

The getProps Method

The getProps method is used to supply data, often fetched from API requests, to routeComponent via the props object.

When routeComponent enhances a component from the routes array, it looks inside the component’s properties for a function named getProps. If you define a function there, it will be exposed as a method of the enhanced component. You don’t have to define the function for every component in the routes array, just the ones where you fetch data before rendering them.

The getProps function that you define is expected to return a promise. When the promise settles, its resolved value is passed to the enhanced component through the props object before the component is rendered.

When a component from the routes array is rendered, the component’s getProps method is supplied with a single JavaScript object. This object has the following properties, depending on the rendering context:

KeyTypeDescriptionAvailabilityMore Information
isLoadingBooleanDescribes the current execution state of the getProps function: true when executing and false when not executing.Client side only
paramsObjectContains object properties that correspond to the named route parameters in an Express-style route string. Example: If you have the route /user/:name, then the value of :name in the request path is available as Default value: {}.Both client side and server sideExpress API → Request → req.params
reqObjectA version of Node’s request object enhanced by Express. Represents the HTTP request and with properties for the query string, parameters, body, HTTP headers, and more.Server side onlyExpress API → Request
resObjectRepresents the HTTP response that an Express app sends when it gets an HTTP request.Server side onlyExpress API → Response
locationStringThe URL of the request.Both client side and server sideNot part of the Express API

Handling Errors

To handle errors in a getProps function, you have two options.

The first option is to throw an HTTPError object, which can be imported from pwa-kit-react-sdk/dist/ssr/universal/errors. When you throw an HTTPError, a dedicated Error component is rendered. (See the section on special components for more about this component.)

The second option is to use props to inform the rendered component of the error so that it can be used in custom error-handling logic.

Here’s an example that uses both error-handling approaches:

Writing getProps Functions

The object returned by getProps is serialized and embedded in the rendered HTML via an object called __PRELOADED_STATE__ in the page source.

To keep the size of the rendered HTML down, be selective in what data to return in getProps. For example, avoid returning the whole response from an API request, if possible.

To preview the version of the page that is rendered on the server side in your browser, append ?mobify_server_only to the URL. This query parameter stops the hydration process so that the browser won’t take over rendering, leaving the page unchanged after server-side rendering. To see a pretty-printed version of the __PRELOADED_STATE__ object, add ?mobify_server_only&mobify_pretty to the query string.

When users navigate to subsequent pages during client-side rendering, the page is rendered immediately. Since rendering can happen while getProps is still fetching data, always write conditional code in your components to handle undefined props. Also remember to render a placeholder component (like Skeleton from Chakra UI) while props are undefined.

The shouldGetProps Method

The shouldGetProps method controls when to call the getProps method. During server-side rendering, shouldGetProps is called only one time. During client-side rendering, it’s called every time the React lifecycle method componentDidUpdate is called.

By default, getProps calls getProps every time location.pathname changes. You can override the default behavior for each component in the routes array by defining your own function called shouldGetProps as a property of the component. You can customize shouldGetProps to inspect the request and only call getProps for certain requests.

Special Components

The PWA Kit React SDK includes the following special components: AppConfig, App, Error, and Document.

The default implementation of each special component can be overridden by your own custom version so that you can configure and customize the behavior of your storefront across multiple pages.

Let’s look at each special component separately:

The AppConfig Component

The AppConfig component is positioned near the top of the component hierarchy in your React app. From this position, it allows you to insert code to configure and support app-wide features, such as state management or component theming. In the Retail React App, the ChakraProvider component is inserted into AppConfig to make theme values (for colors, spacing, and so on) available to all the Chakra UI components.

The AppConfig component also lets you extend the set of arguments that get passed to all components that are enhanced by routeComponent via the getProps function. To add these arguments, define a function called extraGetPropsArgs as a property of the AppConfig component.

The Retail React App uses the extraGetPropsArgs function to give all components that are enhanced by routeComponent access to an object for interacting with the Commerce APIs:

The version of the AppConfig component from the PWA Kit React SDK doesn’t do much—unless you override it. To override the SDK version of AppConfig, define a file in app/_app-config/index.jsx. A newly generated PWA Kit project already includes this file to get you started.

The App Component

The App is a child of the AppConfig component. Its main purpose is to include any components for layout and UI that persist throughout your React app, such as the header, footer, and sidebar.

The App component is also enhanced by the routeComponent function. If you define a getProps function as a property of the App component, it gets called when any component from the routes array is rendered. Use it for any logic that you’d like to run on every page.

Like AppConfig, a default version of the App component exists in the PWA Kit React SDK, and you’re encouraged to override it. To override the SDK version of App, define a file in app/_app/index.jsx. A newly generated PWA Kit project already includes this file to get you started.

Some of the features that are implemented in App for a newly generated project include: analytics tracking, offline detection, and the SkipNavLink component for accessibility.

The Error Component

The Error component is rendered in place of the App component under any of the following conditions:

  • The user makes a request for a path that isn’t found in the routes array.
  • A component from the routes array throws an error in its getProps function.
  • A component from the routes array throws an error during rendering.

When the Error component is returned, an HTTP 404 status is also included in the response header.

Like the other special components, the PWA Kit React SDK includes a default version of the Error component, and you’re encouraged to override it. To override the SDK version of Error, define a file in app/_error/index.jsx. A newly generated PWA Kit project already includes this file to get you started.

By overriding the Error component, you can do things like customize the error page for your brand, add redirects, and track the error as an analytics event.

The Document Component

The Document defines the HTML that surrounds your application, such as the <html>, <head>, and <body> tags.

Like the other special components, the PWA Kit React SDK includes a default version of the Document component that can be overridden. But, in this case, we don’t recommend that you override it. But if you need fine-grained control over your application, you can override Document by defining a file in app/_document/index.jsx. A newly generated PWA Kit project does not include this file.

Rather than override the Document component, we recommend using the React Helmet library to modify the HTML tags in Document, such as <head>.

Next Steps

Get a deeper understanding of rendering and routing by reviewing source code. Here are some key files to check out in the Retail React App:

  • app/ssr.js: contains important app server settings, such as the default cache lifetime for pages.
  • app/routes.jsx: demonstrates the Express-style syntax for path matching, including named route parameters.
  • app/pages/product-detail/index.jsx: this sample component for the PDP includes custom functions for both getProps and shouldGetProps.
  • app/components/_app_config/index.jsx: includes extensive configuration code and an app-wide getProps function.

As you read through the PWA Kit documentation, don’t miss the architecture guide for The Retail React App.