Menu
Menu Sheet Overlay
Search
Search Sheet

      Architecture

      Introduction

      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.

      Router component

      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.

      App component

      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 app/components directory.

      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

      The Redux store contains a complete snapshot of your Progressive Web App at a particular point in time. It contains all of the data needed by your PWA’s React components. This state data is contained in a plain JavaScript object called the app state, which is managed by Redux.

      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.

      Redux actions

      Redux actions are plain JavaScript objects that assist in responding to changes in your app state, whether through user interaction or internal app events. Redux actions describe any changes that need to occur through a string called the type. Optionally, Redux actions can provide data associated with an app state change through an object called the payload. Because Redux actions can only describe changes to the app state, they must be paired with a function called a reducer that is responsible for creating a new copy of the Redux store (with new data in it). Redux actions are invoked by passing them to Redux’s dispatch() function.

      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.

      Thunk actions

      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 dispatch() and 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:

      1. mapStateToProps is a function that links component props to the app state in the Redux store
      2. mapDispatchToProps is a function that links component props to Redux actions so that actions can be dispatched from the component’s event handlers (such as onClick)

      To learn more about binding React and Redux together, visit the React Redux docs.

      Selectors

      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

      Redux requires that the objects in the Redux store be immutable, which means that their values cannot be modified. Instead, objects can be replaced with modified copies. To satisfy this requirement, we use the immutable data structures provided by the Immutable.js library for the data we put in the Redux store. The immutable data structures replace JavaScript’s built-in data types. The replacement for JavaScript arrays is called a list and the replacement for JavaScript objects is called a map. These immutable data structures cannot be compared or modified with standard JavaScript operators like = and ===. 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:

      1. The click handler dispatches a Redux action.
      2. The Redux action uses Commerce Integrations to communicate with the backend to find out how many seats are still available on the flight.
      3. 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.
      4. 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.
      5. If an action has an analytics-meta property, then the Analytics Manager is notified so that the event can be tracked.
      6. 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:

      PWA React Redux Flow

      Mobify PWAs use React and Redux together to manage the app state.

      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

      A tag-loaded PWA is rendered only on the client-side. It’s typically used for projects that are destined for mobile devices—or for both mobile and tablet devices. Its code is contained in a static bundle that’s hosted on Mobify’s content delivery network (CDN) and downloaded onto the user’s device. As the name implies, this type of PWA is loaded by a JavaScript tag, called the Mobify tag.

      Initial request handling and app loading (tag-loaded):

      1. 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.
      2. The ecommerce backend responds with an HTML page that includes the JavaScript for the Mobify tag.
      3. 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.
      4. Mobify’s CDN responds to the request by sending the PWA bundle to the user’s device.

      Tag-loaded PWA Initial Load

      Initial request handling and app loading for a tag-loaded PWA.

      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:

      Tag-loaded PWA Subsequent Load

      Request handling for a tag-loaded PWA after the PWA is running on the user’s device.

      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

      These PWAs are integrated at the DNS level, instead of through a JavaScript tag. For example, a site called www.store.com would point to Mobify’s servers, instead of the ecommerce backend:

      Server-side Rendered DNS Integration

      Server-side rendered PWAs are integrated at the DNS level and point to Mobify’s servers. This is different than tag-loaded PWAs, which are integrated through the ecommerce backend and a JavaScript tag.

      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:

      Server-side Rendered PWA Initial Load

      Initial request handling and app loading for a server-side rendered 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:

      Server-side Rendered PWA Subsequent Load

      Request handling for a server-side rendered PWA after the PWA is running on the user’s device.

      Commerce Integrations

      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:

      Commerce Integrations

      Commerce Integrations provide a layer of abstraction between your PWA and your site’s backend platform.

      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:

      Analytics Manager

      The Analytics Manager is an abstraction layer between your PWA and your site’s analytics platform or platforms.

      To learn more, visit our Analytics Manager docs.

      Mobify Cloud

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

      Next steps

      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.

      IN THIS ARTICLE: