Progressive Web Apps (PWAs) created with the Mobify Platform are single-page apps that deliver fast, immersive user experiences across devices. This isn’t your typical web build: single page apps have more in common with native apps than they do with traditional websites.
In this overview, we’ll highlight the key technologies that make up the Mobify Platform and explain several concepts that will help you understand its architecture. You’ll learn how:
- A Mobify PWA is made up of different types of React components
- App state is managed with Redux
- Server-side rendered PWAs are different from tag-loaded PWAs
- Mobify provides a data layer for connecting to ecommerce backends and another for analytics services
React component hierarchy
A Mobify PWA is built from a hierarchy of React components. In this section, we’ll walk you through the Router and App components at the top of the hierarchy and we’ll look at the UI components you’ll include on your pages.
At the root of the hierarchy we have the Router component, defined in
app/router.jsx. The Router component wraps the entire application and is responsible for routing URLs to your page components.
As a developer working with the Mobify Platform, you’ll need to edit the
app/router.jsx file regularly—anytime you add a new page, for example.
By itself, the Router component does not render any visible components or UI. Instead, it defines the application’s routing structure. The purpose of the Router component is to:
- Create and wrap the application in the Redux store provider
- Map URL paths to React components using React Router
Router.jsx has all the different page routes for the application as its children, and it renders the appropriate page based on the current URL.
To learn more about routing, read our guide to Routing URLs.
The App component renders the app’s global UI, such as your site’s header, or its sidebar navigation. It’s defined in
app/index.jsx. When any given route is rendered, it renders with App as its top-level component.
As a developer working with the Mobify Platform, you’ll rarely need to edit the App component. You may do so during initial configuration of the site, or you may use it to modify the global level of your application.
This is where app-level elements, such as the SVG sprite sheet or the SkipLinks component for accessibility are rendered. In addition, the App component handles offline mode detection, rendering an OfflineBanner component with an offline status screen if a page is not available.
User interface components
Throughout your build, you’ll be working with Mobify’s SDK user interface (UI) components, as well as building your own custom UI components. These UI components are the building blocks of your app and its pages.
Mobify’s SDK UI components can be found in your project’s
src/components directory. By using the SDK components, you inherit a set of props and methods for pre-built UI features, designed for accessibility and lightning-fast performance. For example, you can customize a pre-designed Carousel, Banner, Image—or any of the 70+ components we offer. Check out our detailed documentation about our component library.
You can also develop completely custom UI components for your project. These custom components can be found in the
When creating custom components, run the following command to generate all the files you need in the right place:
$ npm run add:component
Putting it all together
Now that we’ve seen each component in the hierarchy, let’s review how they build upon each other:
- The Router component wraps the entire app and defines the URL routing for the app.
- The App component is rendered on all routes and manages the global UI.
- SDK components and custom components render the UI of the app and its pages.
Managing app state with Redux
In the Mobify Platform architecture, React components rely on Redux to manage their state. In this section, you’ll learn:
- Essential concepts, such as the Redux Store, Redux actions, thunk actions, and selectors
- How to ensure that app data doesn’t change unexpectedly with Immutable.js
The Redux store
To learn more about the Redux store, visit the Redux docs.
Note: We tend to use the following terms interchangeably: Redux store, store, app state, and state.
To learn more about Redux actions and how they work together with reducers, visit the Redux docs.
Note: We tend to use the following terms interchangeably: Redux action, standard Redux action, and action.
A thunk action is a special type of Redux action that is defined as a function, rather than an object. This is useful when you need to run asynchronous code (to fetch data from the backend, for example). Thunk actions also have the ability to dispatch standard Redux actions so that a new app state can be created by a reducer after running any asynchronous code. They can also access data from the Redux store using Redux’s
getState() function. Thunk actions are invoked the same way as standard Redux actions then handled by the Redux Thunk middleware, which supplies the thunk action’s function with additional parameters, including the
getState() functions from Redux.
Binding React and Redux together
We use the React Redux binding library to make the Redux store and Redux actions accessible to your React components through props. React Redux provides a function called
connect() that takes two parameters that define new Redux-related props for your component:
mapStateToPropsis a function that links component props to the app state in the Redux store
mapDispatchToPropsis a function that links component props to Redux actions so that actions can be dispatched from the component’s event handlers (such as
To learn more about binding React and Redux together, visit the React Redux docs.
A selector is a pure function that takes an app state object from the Redux store and returns some information extracted from that state object. Most often, selectors are used in mapStateToProps functions to provide data to your React components.
In a Mobify PWA, we use selectors whenever we need to access data from the Redux store, rather than accessing the Redux store directly. This allows us to change the structure of the store without having to update every mapStateToProps function that accesses the store. Instead, we just update any selectors that are affected by the change.
Memoization and the Reselect library
The Reselect library provides a number of useful features for creating selectors. Most importantly, selectors built with Reselect are memoized. A memoized function has a memory: it keeps track of the previous arguments that were passed into it and keeps track of the previous result. If the function is pure and the inputs do not change between sequential calls to the function, we don’t have to execute the body of the function more than once. Memoization helps avoid unnecessary re-renders.
To learn more, visit the Reselect library Github.
Immutable Data Structures
===. Instead, Immutable.js provides functions and methods for comparing objects and creating modified copies of them.
To learn more about working with these data types, visit the Immutable.js docs.
The PWA Lifecycle
Let’s examine how a Mobify PWA manages state through an example.
Imagine that you’re building a PWA for an airline. The shopper on your site interacts with UI elements created with Mobify’s SDK components. A shopper clicks on the banner of your PWA, which advertises a holiday sale for a flight to Warsaw. (Banner is one of Mobify’s SDK components.) Now we want to fetch information from the backend about the sale and present it to the user in a modal. Here’s what happens in the app:
- The click handler dispatches a Redux action.
- The Redux action uses Commerce Integrations to communicate with the backend to find out how many seats are still available on the flight.
- Another Redux action is dispatched with the data from the backend included in the action’s payload. To improve performance, the Service Worker can also be used to cache responses from the backend.
- The reducer associated with the action will merge the new data into the Redux store. Now it contains data about the number of seats available.
- If an action has an
analytics-metaproperty, then the Analytics Manager is notified so that the event can be tracked.
- A selector that returns the seat data from Redux store is mapped to one of the props in a UI component. Whenever the props for a component change, the component will re-render so the UI component will now show the number of available seats.
To summarize the flow that just occurred, let’s look at it in a diagram:
Two types of PWAs
Mobify PWAs can be delivered in one of two ways: tag-loaded or server-side rendered.
Let’s take a look at each delivery method in more detail…
1. Tag-loaded PWAs
Initial request handling and app loading (tag-loaded):
- The user navigates to the website using their mobile or tablet device, which sends a request to the site’s web server, which is usually connected to an ecommerce backend.
- After receiving the page, the device parses the HTML. When the parser reaches the Mobify tag, the device will request the code bundle for the PWA from Mobify’s CDN.
- Mobify’s CDN responds to the request by sending the PWA bundle to the user’s device.
Subsequent request handling (tag-loaded)
After the initial page load, the PWA is now running on the device, so it can make requests directly to the ecommerce backend—bypassing the Mobify CDN.
Here’s a visual representation:
2. Server-side rendered PWAs
Rendering PWAs on the server-side has a couple of important benefits over tag-loaded PWAs. First, the server enables even faster load times, with the ability to cache previously-loaded responses. Second, server-side rendered PWAs can help manage the complexity of multiscreen layouts. These PWAs can render content destined for any device, whether it’s mobile, tablet, desktop, or even something that hasn’t been invented yet.
Server-side rendered PWAs have a code bundle just like tag-loaded PWAs do. But in this case, the bundle is run on Mobify’s servers and usually served from a cache to maximize speed.
Integrating with DNS
Initial request handling and app loading (server-side rendered)
- The user navigates to the website using their mobile, tablet, or desktop device, which sends a request directly to the Mobify CDN.
- Assuming we don’t already have anything in the cache, the Mobify CDN will then request the PWA from the server side rendering (SSR) component.
- From here, Commerce Integrations helps the SSR component communicate with the ecommerce backend.
- Using the data from the ecommerce backend and Mobify’s SDK UI components, the SSR component renders the PWA. This will not include any personalized content, such as the user’s shopping basket data. Not including personalized data allows us to take advantage of caching to speed up each subsequent request.
- The Mobify CDN receives the PWA without personalized content and caches it.
- The Mobify CDN responds to the user’s request by sending the PWA bundle.
To summarize, here is a diagram that shows the server-side rendered PWA lifecycle, for the first time a device requests the PWA:
Subsequent request handling (server-side rendered)
Once the server-side rendered PWA is running on the device, handling every subsequent request becomes faster:
- Similar to a tag-loaded PWA, the server-side rendered PWA can now communicate directly with the ecommerce backend, using Commerce Integrations
- In certain situations, requests may be proxied through the Mobify CDN to communicate with the ecommerce backend.
Both types of request handling are captured in the following diagram:
Mobify’s Commerce Integrations technology provides a layer of abstraction between your PWA and the ecommerce backend of your site, such as Salesforce B2C Commerce. Through commerce connectors (think of them as plug-ins), you get a standard interface to communicate with any ecommerce backend. The commerce connector uses a class whose methods standardize common actions to fetch and update ecommerce data—plus you can extend the class to work with your project’s unique backend.
Both types of request handling are captured in the following diagram:
To start learning about using Commerce Integrations in your project, check out our Commerce Integrations docs.
The Analytics Manager
The Analytics Manager provides a layer of abstraction between your PWA and your site’s analytics providers, such as Google Analytics. This is similar to Commerce Integrations, but for analytics.
The Analytics Manager listens for special Redux actions that your PWA dispatches and detects analytics events from them. From there, it sends that information to each of the analytics services that you’ve connected to the Analytics Manager.
This data flow is captured in the following diagram:
To learn more, visit our Analytics Manager docs.
Mobify Cloud simplifies the deployment of both tag-loaded PWAs and server-side rendered PWAs. It allows you to:
- Deploy bundles in three convenient ways: either using Mobify Cloud’s web interface (cloud.mobify.com), using your favorite command line interface, or via HTTP API.
- Push bundles for testing
- Roll back to previous bundles if necessary
- Set up various environments for your project where you’d like your bundles to be deployed. For example, staging, production, or user acceptance testing (UAT).
After reviewing this architecture overview, you should now have an understanding of the technologies powering the Mobify Platform, and you can start applying some of the key concepts you’ll encounter throughout your build.
To continue your learning journey, try one of our hands-on exercises in Guides.