Table of Content

How to Develop a Store Using Magento PWA Studio (Updated for 2024)

PWA-studio.jpg

Progressive web applications are growing in popularity by the month, particularly in eCommerce. More and more online retail businesses are opting for PWA storefront development on Magento (Adobe Commerce). This modern web technology turns an eCommerce store into a user-friendly web app with an app-like experience, giving you more chances to make prospects happy and encourage them to buy.

In this blog article, we’ll introduce you to everything you should know about building a PWA with the use of the PWA Studio tools. We’ll explore the pros and cons of using the Magento 2 PWA Studio and explain what to keep in mind when working on a Magento PWA studio project. Then, we’ll walk you through the steps for building the progressive web app with code examples and detailed instructions. As a final touch, we’ll share handy suggestions from the pros to learn more about setting up your Magento PWA Studio.

What to Expect From This Magento PWA Studio Guide

A Quick Introduction to Progressive Web Apps

websites are becoming a real hit. The number of users who shop from their mobile phones is continuously increasing, contributing to the growth of mobile commerce. In essence, progressive web apps are not actually applications that we’re used to in the conventional sense. It’s a very modern, fast, and highly-optimized mobile version of a website.

And progressive web apps, undoubtedly, have many benefits over good-old native applications; here are some of them:

  • PWAs are fast and efficient;
  • They function in web browsers like Chrome, Safari, and Opera and are accessible from search engines;
  • They don’t have to be downloaded to the device from Google Play or the AppStore;
  • They practically take up zero storage space on the user’s device;
  • They have the same impeccable UX and UI as native apps and are very simple to navigate;
  • They support the offline mode due to caching static files;
  • And generally, less in terms of web development (at least if compared to native apps. You have to build native apps up as at least two different versions in terms of coding languages to fit the standards of iOS and Android, to provide an example).

In a nutshell, this, to a great extent, explains the current high demand for progressive web apps in the sphere of eCommerce and online sales. Put simply, everything used in the PWA leads to a boost in how convenient the is for users.

In turn, this helps lower the bounce rate, enhance the conversion rate, and result in revenue growth. So, a growing number of online store owners wish to invest in their own progressive web apps.

One of the reasons for getting a PWA storefront project lies in the architectural transformation the store undergoes. Known as , this concept involves separating the frontend from the backend and moving from mere server-side rendering to the client-side one from the second visit. It provides various benefits to store owners, like faster page loading and the ability to add more fronts to a single codebase.

So opting for headless commerce in the form of first getting a PWA storefront now is also a step helping merchants prepare their architecture for the future. As new devices will become available for shopping, the UI/UX requirements may be different due to users’ various behaviors and device peculiarities.

But what do you have to understand about the PWA development process?

The Approaches to PWA Development

Let’s begin by making it clear that there are multiple on Magento. Just as in the case of the construction of buildings, PWA developers can opt for several different paths to reach more or less the same result:

  1. via custom coding using a progressive frameworks (just plain React.js, Angular.js, or Vue.js);
  2. Building the progressive web app by customizing a ready-made PWA theme (these are the PWA Studio that’s officially brought by Magento, or other third-party themes like the Scandi PWA toolkit or the Vue Storefront, to give a couple of examples);
  3. Opting for a combination of custom coding and theme customization (this supposes a blend of the two points above).

If you are planning to create a highly customized Magento PWA storefront, you can't rely on the developer tools provided by the PWA theme only. Plain ReactJS coding would be the best option here. If your dev team doesn't have a good command of React, turn to our , who are clearly aware of how the PWA technology functions.

What This Magento PWA Studio Tutorial Focuses On

Now that this is clear, we’d like to point out that this blog post is devoted to progressive web apps created with the help of the Magento PWA Studio.

For this specific case, we’ll operate from the idea that your Magento online store functions on Magento 2 (either open source or hosted) that’ll be fitted with a PWA. Whether you've migrated from M1 or created the website on M2 from the beginning, you'll find this guide helpful if you’d like to opt for a solution and get a Magento PWA storefront.

In this blog article, we’ll cover the following things:

  • Give an introduction to the Magento PWA Studio tools, as well as the main strengths and weaknesses of their use.
  • Go over the UX and UI peculiarities that have to be taken into account when creating a progressive web app.
  • Provide step-by-step instructions in the Magento 2 PWA Studio tutorial block. We’ll be creating a homepage of a PWA storefront using Magento PWA Studio's tools (providing examples of code).
  • Bring up some points regarding the work with the Magento backend if you build the web app via the Magento 2 PWA Studio.
  • Lastly, we’ll share recommendations on launching the Magento PWA Studio project, as well as which errors you can bypass when working with the PWA Studio.

Let’s get started!

1. What Is Magento PWA Studio?

As was briefly mentioned a bit earlier, the Magento 2 PWA Studio is a toolkit. It was designed by the official Magento team specifically to help developers build progressive web applications on the Magento 2 platform. The tool comprises ready-to-use, out-of-the-box PWA solutions that can come in handy during web development. Plus, it eliminates the possibility of code conflicts and allows a PWA to run without a hitch, thanks to its built-in consistency with the Magento coding standard.

Yet if everything’s available out of the box, why do developers even bother to code everything (or partially) from scratch? Well, to be fair, the Magento PWA Studio has its own benefits and weaknesses. Below, we’ll tell you more about them.

Advantages of Using the Magento PWA Studio

1. The Magento PWA Studio is not monolithic

This surely is a beneficial thing that simply means that the available solutions of the Adobe PWA Studio for Magento are flexible and can be used partially. You won’t need to resort to the entire code in an obligatory manner. For instance, you can make use of just some of its parts, such as Peregrin or the PWA Buildpack.

2. The app’s architecture and framework are out-of-the-box

This is a very crucial point worthy of note. The thing is that developers often end up atoning for their errors if they’ve made irrational choices when building the application’s architecture at an early app development stage. If something has illogical organization, and this pops up later in the development environment, a lot of time will be needed for the fixes.

Yet, it's not an issue with the Studio. When the architecture is available as a skeleton and has built-in consistency with the Magento coding standard, consider that this point is already safely taken care of. Thus, this is among the handiest Magento PWA Studio features that benefit the developers.

3. The application builder is already configured

Having the opportunity to save some development hours and time on handling the setups of the application builder is another bright side of the PWA Studio. Everything in the app builder is already configured for you to use.

4. The ready-made app elements are adaptable

The PWA Studio has quite an extensive range of ready-made site elements you can choose from and use during development. The greatest thing about them is that they can be used entirely as they are. Yet, if necessary, the elements can be modified and customized according to the needs (be they due to design or those changes required solely for app development).

5. Routing & caching can be used without being tweaked

The matters of routing and caching are also the PWA Studio's strong points. These solutions are provided so that there’s no need to tweak or modify them. Note that this applies to the case if you’re using the standard Magento-way routing in your PWA Studio project.

6. The service workers are already set up & out-of-the-box

The Service Worker is a commonly used browser resource caching tool on the Magento PWA. The Magento 2 PWA Studio has already taken care of the time-consuming setup work and is in its ready-to-run shape.

Downsides of Using the Magento PWA Studio

1. Code excessiveness

Because the Magento team did their best to develop more or less universally applicable solutions, this obviously resulted in lots of excessive code. This basically means that developers will need to invest their time in “cutting out” those parts that aren’t relevant to their development environment. Why is this step required? To say the least, excessive code negatively influences page loading.

2. Code complexity due to abstractions

Ready-made Magento PWA Studio code solutions mean that their use often implies the reuse of unwanted abstractions or probable bugs. This also means that the code is, at times, complicated, inconvenient in some places, and, thus, takes time to figure out and untangle.

That said, keeping in mind particular specifics of the Magento store that you're building, many solutions by the Magento 2 PWA Studio can be unnecessary for your case. For instance, if your web application doesn't need a product cart, the provided functionality will be out of place. This will lead you to cut out the code and contexts that don't fit your PWA storefront project.

3. Lots of time for studying the ready-made code

When provided with an out-of-the-box solution, in the app development case, you should be looking a gift horse in the mouth. Developers make every effort to take their time and study the existing code before they get their hands on creating their own. Primarily, this is done so as not to end up reinventing the wheel (writing code for something that already exists).

2. Creating Ultimate UX/UI for a Flawless Magento 2 PWA

Because has to offer amazing navigation, it’s quite self-explanatory that the app’s UX and UI have to be given much consideration.

To say the least, if you’re migrating a website to Magento 2, your old design is a no-go. And even if you have worked on optimizing your mobile design, it’ll still most likely need some additional improvement because of the high UX/UI standards for PWA creation.

To give you a few examples of the trends designers tend to follow lately, it has become customary to place crucial page elements closer to the bottom of the screen. Such elements can include buttons, site menus, drop-down lists, pop-ups, etc.

This is done to make all the major “touchpoints” thumb-reachable. Especially, if you keep in mind that the screen sizes of mobile devices are getting bigger, meaning that element placement at the top of the screen will require “unnatural” movements. Feel free to browse these for some inspiration.

Thus, just shrinking the desktop version to fit mobile devices is, by all means, not enough. Accessibility and reachability are some of the key points to note when working on Magento 2 PWA designs. Below we’ll list more principles that are best to follow.

Must-Follow UX/UI Principles

It’s not just about what your PWA will look like on a mobile screen. It’s all about how it will behave and how user-friendly and easy it will be to use. These are the 10 main rules not to forget about when creating prototypes for the future Magento 2 PWA:

  • Achieving the shortest possible page load times should be among the top goals.
  • Dedicate a separate pop-up or screen for a new action on the page.
  • Site navigation and any move should be easy for the user.
  • Try to stick to simpler and more basic fonts and visual components.
  • Those parts of the page that can be tapped on should be highlighted for visual help.
  • Major elements that can be clicked on should be positioned in the lower part of the screen.
  • Remember that some devices can, by default, take up half of the screen if the user opens their keyboard.
  • Fall back on universally-applicable UX/UI “rules” that regard different sizes of screens.
  • Don’t forget about how the pages will differ if used offline.
  • Ideally, data should be refreshed without a full page reload.

UX/UI for a PWA on Examples

In order to not multiply words, we’ve decided to provide you with some visually understandable examples. We’ve overviewed the mobile versions of several page types of various websites. For each of them, we have prepared our own prototypes. They portray how the current design could be improved if the site owners would request a Magento 2 PWA storefront. To make things clear, we’ve explained each of our UX/UI choices that you can see in the comments below the prototypes. We also have a dedicated article with , showing how other companies leverage PWAs for a better online experience.

a) Homepage - House of Fraser

Let’s start by taking a closer look at the homepage example. We’ve chosen the one on the . In the table below, we present you with the whole original design and the full prototype that you can scroll through.

How the current mobile design looks like (full screenshot)Our full prototype for the PWA mobile design

House of Fraser homepage screenshot 1

House of Fraser homepage screenshot 2

House of Fraser homepage screenshot 3

House of Fraser homepage screenshot 4

PWA prototype by Onilab for the House of Fraser homepage

pwa prototype by Onilab for the House of Fraser homepage

Below, we’ve broken down the designs into pieces and will share our ideas on what should be changed and why.

How the current mobile design looks likeOur prototype for the mobile design of the PWA

Homepage screenshot - House of Fraser

PWA prototype for the House of Fraser homepage by Onilab

  • When we’re taking a homepage into consideration, it’s important to let the shoppers know that the application is available for download at the very top of the page. This is why we’ve moved the rounded elements with Google Play and App Store logos all the way from the bottom of the page (where they’re located on the original design).
  • Plus, we removed the unnecessary first stripe that duplicates the special offer (“70% off reduction”) since it’s described in the main banner. On a side note, it makes sense to display this specific stripe at the top of other page types, such as Categories, to serve as a reminder.
  • What’s for the second stripe (with the “£10 voucher”), we’ve decided to leave it as it is on the homepage yet added the “Close” button. Such advertisement stripes and larger banners are often very distracting, especially if presented in GIF format. Thus, there should be an opportunity to close them.
  • The main banner with the “Sale” categories was extended into more than one scroll on the initial design. Yet, it doesn't comply with the mobile application approach. Its main idea lies in the fact that one screen should fit a single section without needing to scroll down. With that in mind, we’ve narrowed the block down with the “Sale” categories to just one screen.
How the current mobile design looks like (full screenshot)Our full prototype for the PWA mobile design

A screenshot of the House of Fraser homepage

A screenshot of the House of Fraser homepage 2

PWA prototype for the House of Fraser homepage by Onilab

  • This homepage design has product categories lined out one by one. This doesn’t make too much sense, so we’ve reassembled them into smaller category blocks.
  • For the same reasons, we’ve reorganized the items presented in the “Don’t Miss” section into a slider format.
How the current mobile design looks likeOur prototype for the mobile design of the PWA

House of Fraser homepage screenshot

pPWA prototype for the House of Fraser homepage by Onilab

  • On a similar note, the current mobile design has an inexplicit swiper design of the “Top Sale Picks”. As you can notice, it seems as though there’s just one item. By making the second top-pick product “peek” on the right side, we made the slider more apparent.
  • Last but not least, the “Stay in the Know” section for email sign-up has a strange “Men/Women” choice solution. We’ve decided to use a checkbox to make it not look like buttons with links that can lead you to some other page.

b) Category Page - Costco

The category page example we’d like to cover in our overview is the . In the first table that follows, you can find the whole version of the prototype and the original design.

How the current mobile design looks like (full screenshot)Our full prototype for the PWA mobile design

Costco category page screenshot 1

Costco category page screenshot 2

Category page PWA prototype by Onilab

Category page PWA prototype by Onilab

  • As you can see, the header area is very large in the original layout. We’ve decided to minimize many areas, for example, the stripe with the COVID text that takes up too much space. Likewise, we moved the “Set Delivery ZIP Code” to the section by the filter and sorting.
  • Because the ongoing design doesn’t allow changing the layout from a listing to a grid, we’ve added this point for navigation convenience.
  • The “Member Only Item” block solution is also quite crude. For starters, we added the “Special Event” label atop the picture and the star rating, too. Furthermore, instead of having a field that states that “More colors are available”, we added more useful functionality that actually shows the color variations, materials, and sizes that a product can come in. Importantly, since the price isn’t visible on such items, we’ve made it more obvious that you have to sign in to see the item price.
  • The wish list icon that’s used on the category is irregular. It looks like a bulleted list with a tiny “plus” sign in the top corner. We decided to change how it looked and moved the wish list functionality to the product page altogether.
  • For convenience, we added the feature for autoloading products as the user scrolls the category and an indicator for the page number.
  • Mentioning the “Related Categories” section, we believe that this block isn’t that necessary here as all the categories are readily available on the menu.
How the current mobile design looks likeOur prototype for the mobile design of the PWA

Costco category page screenshot 3

Category page PWA prototype by Onilab

  • is also worth changing. In the original design, the filter doesn’t open to the full scale of the page. We expanded it to the full screen so that all options are easier to tweak. We also changed the way the price range is indicated. We allowed manual input or moving around the price range instead of selecting it as a checkbox. What is more, we now show the matching results at the bottom. Pressing the “Apply” button puts the filter in action and closes the filter section.
How the current mobile design looks likeOur prototype for the mobile design of the PWA

Costco category page screenshot 4

Category page PWA prototype by Onilab

  • Lastly, the design of the “Sort by” element is a bit awkward. It takes up a full line and has an inconvenient drop-down. Our variant with a button that opens as a pop-up is much more user-friendly. This way, the options are easier to tap on, and when selected, the sorting is applied.

c) Product Page - Rebecca Atwood

Thirdly, what’s for our product page, we’d like to overview the . Just as in the two previous examples, we show you the full version of the designs and then the broken-down pieces with our ideas on improving the Magento 2 PWA product page presentation.

How the current mobile design looks like (full screenshot)Our full prototype for the PWA mobile design

Rebecca Atwood product page screenshot 1

Rebecca Atwood product page screenshot 2

Rebecca Atwood product page screenshot 3

Rebecca Atwood product page screenshot 4

Product Page PWA prototype by Onilab for the Rebecca Atwood site

Product Page PWA prototype by Onilab for the Rebecca Atwood site

Once again, below, we’ve broken down the designs and given comments. 

How the current mobile design looks likeOur prototype for the mobile design of the PWA

Rebecca Atwood product page screenshot 5

Product Page PWA prototype by Onilab for the Rebecca Atwood site

  • The first point we decided to change was how the breadcrumb path was displayed, and we shortened it to one line.
  • The size of the price digits is very small and needs to be enlarged. This is needed because how much an item costs is a very important point.
  • The element for choosing the item’s size is located too low, although it’s more important than the product description text. We moved it up and got rid of the drop-down, using buttons for every separate variation instead.
  • We also moved the “Quantity” element up and introduced the “+/-” for indicating the number of items. This is much simpler than using the keyboard for inputting the digit.
How the current mobile design looks likeOur prototype for the mobile design of the PWA

Rebecca Atwood product page screenshot 6

Product Page PWA prototype by Onilab for the Rebecca Atwood site

  • Characteristics and details located below the description don't take up much space, so there’s no need to hide them in pop-ups. We rearranged the layout of the text in a more readable way.
  • The “Delivery Details” is a hardly visible element. The color is not the best choice. We placed it into a visible section showing the details in a pop-up.
How the current mobile design looks like (full screenshot)Our full prototype for the PWA mobile design

Rebecca Atwood product page screenshot 7

Rebecca Atwood product page screenshot 8

Product Page PWA prototype by Onilab for the Rebecca Atwood site

Product Page PWA prototype by Onilab for the Rebecca Atwood site

  • The original design is missing product reviews. We believe that even if there isn’t any feedback on the item yet, it’s crucial to have this section to encourage users to add reviews.
  • The “Related” products are placed quite inconveniently on the ongoing version of the page. A slider format can make such data look a lot more organized.
  • Furthermore, we’ve decided to cut down the space the newsletter sign-up block took up.
  • Lastly, we believe that the social media shortcuts belong in the footer area, so we moved them down. Similarly, we reworked the footer area a bit so it is consistent, simple to navigate, and to the point.

If you are stuck in the stage of creating UX prototypes for your future Magento 2 PWA storefront, our experienced  are ready to offer a helping hand. We know how to make an enhanced UX and give your mobile users a native-app-like experience. With this combo, you can increase your conversions and reduce acquisition costs.

3. Building a PWA Using the Magento PWA Studio: Frontend Works

Now that the design part of the progressive web app is clearer, let’s move on to the section of this Magento 2 PWA Studio guide devoted to creating a PWA with the use of the PWA Studio's tools.

Objectives

Let’s begin by lining out what we’d like to accomplish. Our major goal is to build a Magento 2 PWA storefront with the help of the Magento PWA Studio toolkit. We’ll craft only the homepage and make the PWA theme more adapted for desktop and mobile storefronts. The reason is that the out-of-the-box theme is geared towards the mobile-first design. We plan to:

  1. Replace the standard out-of-the-box Venia storefront logo with our own.
  2. Add an expanded category menu (since in the Magento 2 PWA Studio, it comes in a format that’s tailored to mobile devices).
  3. Move the sign-in authentication into a pop-up.

Step 1: Getting Started

a) Configurations & setups preparation

We’ll run the web app on Ubuntu 18.04. Start by handling the required configurations on Ubuntu 18.04, install node JS and the needed auxiliary software.

sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates
curl -sL //deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt install nodejs

We also need the Yarn package manager. This Javascript package manager is noted for being a fast, safe, and trustworthy analog of NPM. It is capable of substituting the workflow that's currently used for the npm client, and it can support the current npm registry.

curl -sS //dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb //dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn

b) Creating a new project

Now move on to the Magento 2 PWA Studio setup by creating a new project with the help of the PWA Studio Buildpack, as shown in the code below. Being one of the Magento PWA Studio essential packages, it offers the key build and development tools for your PWA storefront, including project setup tools, configuration management, and Magento extension frameworks.

One of the parts of the PWA Studio that's shown in the code example below is the Venia PWA storefront. It's an initial Magento PWA storefront, which is responsible for displaying components.mkdir venia-create-app && cd venia-create-appyarn create @magento/pwa.

After running the previous commands, you can move step by step and fill out all the values for the buildpack command. Alternatively, use the following code line to replace the variable values with your own.

buildpack create-project ./ --template "venia-concept" --name "pwa-studio-shop" --author "onilab" --backend-url "//master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/" --braintree-token "sandbox_8yrzsvtm_s2bg8fs563crhqzk" --npm-client "yarn"

Creating a PWA Studio project code example

After installation, we get the following file structure:

Creating a PWA Studio project file structure example

c) Overriding inner components

For further development based on the Venia Concept by the Magento PWA Studio, we need a mechanism for replacing the internal components with our own. For this reason, we need:

  • a Webpack plugin that will perform component substitution,
const path = require('path');
const glob = require('glob');

module.exports = class NormalModuleOverridePlugin {
   constructor(moduleOverrideMap) {
       this.name = 'NormalModuleOverridePlugin';
       this.moduleOverrideMap = moduleOverrideMap;
   }
   
   requireResolveIfCan(id, options = undefined) {
       try {
           return require.resolve(id, options);
       } catch (e) {
           return undefined;
       }
   }
   
   resolveModulePath(context, request) {
       const filePathWithoutExtension = path.resolve(context, request);
       const files = glob.sync(`${filePathWithoutExtension}@(|.*)`);
       if (files.length === 0) {
           throw new Error(`There is no file '${filePathWithoutExtension}'`);
       }
       if (files.length > 1) {
           throw new Error(
               `There is more than one file '${filePathWithoutExtension}'`
           );
       }
       return require.resolve(files[0]);
   }
   
   resolveModuleOverrideMap(context, map) {
       return Object.keys(map).reduce(
           (result, x) => ({
               ...result,
               [require.resolve(x)]:
               this.requireResolveIfCan(map[x]) ||
               this.resolveModulePath(context, map[x])
           }),
           {}
       );
   }
   
   apply(compiler) {
       if (Object.keys(this.moduleOverrideMap).length === 0) {
           return;
       }
       
       const moduleMap = this.resolveModuleOverrideMap(
           compiler.context,
           this.moduleOverrideMap
       );
       
       compiler.hooks.normalModuleFactory.tap(this.name, nmf => {
           nmf.hooks.beforeResolve.tap(this.name, resolve => {
               if (!resolve) {
                   return;
               }
               const moduleToReplace = this.requireResolveIfCan(
                   resolve.request,
                   {
                       paths: [resolve.context]
                   }
               );
               if (moduleToReplace && moduleMap[moduleToReplace]) {
                   resolve.request = moduleMap[moduleToReplace];
               }
               
               return resolve;
           });
       });
   }
};
  • a file for mapping our components and those of the Magento PWA Studio,
module.exports = componentOverride = {};
  • to add everything to the Webpack configuration file (webpack.config.js).
...
const moduleOverridePlugin = require('./moduleOverrideWebpackPlugin');
const componentOverrideMapping = require('./componentOverrideMapping');
...
module.exports = async env => {
   ...
   clientConfig.plugins.push(
       new moduleOverridePlugin(componentOverrideMapping)
   );
   
   ...
};

d) Project folder

Create a folder that’ll contain our future components.

сd venia-create-app/src && mkdir components

As a result, we get the following web pages. Below you can take a look at what the Venia PWA storefront home page looks like after installation.

Venia PWA Studio homepage example

Step 2: Changing the PWA Logo

Now we’ll give you a detailed example of how to make changes to the . To start, we’ll replace the logo.

a) Copy the component folder completely

To make any changes, you need to copy the entire component to your folder. In the following example, we show how you can override an entire component via component override.

Copy the component from:

venia-create-app/node_modules/@magento/venia-ui/lib/components/Logo

To:

venia-create-app/src/components/Logo

b) Set up the override components

Carry out the setups in componentOverride. Here we add the override component for the module. Now instead of searching for the component the standard way, Magento will search for it in the way we've specified in this example:

module.exports = componentOverride = {
   ['@magento/venia-ui/lib/components/Logo']: 'src/components/Logo',
};

c) Replace relative paths with absolute ones

In order for our component to have the ability to use the Magento components, replace the relative paths with absolute paths, as shown in this example for the logo.

Change this part:

import { mergeClasses } from '../../classify';
import Image from '../Image';
import logo from './VeniaLogo.svg';

To look like this:

import { mergeClasses } from '@magento/venia-ui/lib/classify';
import Image from '@magento/venia-ui/lib/components/Image';
import logo from '@magento/venia-ui/lib/components/Logo/VeniaLogo.svg';

d) Change the logo

Remove the Venia storefront logo that the Magento 2 PWA Studio provides and upload your own logo instead, using your file.

import logo from './onilab-logo.svg';

Step 3: Rebuilding the Menu from Its Mobile Version to the Desktop Format

Moving on to a more complex example. By default, all menus (even the desktop menu) are hidden in the Venia PWA theme. So we need to reveal it.

a) Create a new component

Let's create a new component that will display categories. Follow the example below:

.root {
   margin: 0 12rem
}

.link {
   margin: 0 1.2em;
}

import React from 'react';
import {useCatalogContext} from "@magento/peregrine/lib/context/catalog";
import { Link } from '@magento/venia-drivers';
import classes from './topCategoryNav.css';

const TopCategoryNav = props => {
   const [catalogState, { actions: catalogActions }] = useCatalogContext();
   const { categories, rootCategoryId } = catalogState;
   
   let topCategories = [];
   
   Object.keys(categories).forEach(key => {
       if (categories[key].parentId == rootCategoryId && categories[key].include_in_menu == 1) {
           topCategories.push(categories[key]);
       }
   });
   
   const navigation = topCategories.map(item => (
       <Link key={item.id} className={classes.link} to={item.url_path.concat(item.url_suffix)}>
           <span>{item.name}</span>
       </Link>
   ));
   
   return (
       <div className={classes.root} >
           {navigation}
       </div>
   );
}

b) Add the component to the menu

The next step is to add the component to the menu. To do this, replace and edit the header components.

Copy this file from:

venia-create-app/node_modules/@magento/venia-ui/lib/components/Header/header.js

To:

venia-create-app/src/copmponents/Header/header.js

c) Replace relative paths & swap the menu

Add the code that’ll allow you to hide the trigger of mobile navigation on the desktop version. Then add the code to display our new component on the output of menu categories.

import React, { Suspense } from 'react';
import { shape, string } from 'prop-types';

import Logo from '../Logo';
import { Link, resourceUrl, Route } from '@magento/venia-drivers';

// Modified by Onilab - Replace relative path with full path -- start
import AccountTrigger from '@magento/venia-ui/lib/components/Header/accountTrigger';
import CartTrigger from '@magento/venia-ui/lib/components/Header/cartTrigger';
import NavTrigger from '@magento/venia-ui/lib/components/Header/navTrigger';
import SearchTrigger from '@magento/venia-ui/lib/components/Header/searchTrigger';
import OnlineIndicator from '@magento/venia-ui/lib/components/Header/onlineIndicator';
import { useHeader } from '@magento/peregrine/lib/talons/Header/useHeader';

import { mergeClasses } from '@magento/venia-ui/lib/classify';
import defaultClasses from '@magento/venia-ui/lib/components/Header/header.css';
import PageLoadingIndicator from '@magento/venia-ui/lib/components/PageLoadingIndicator';
// Modified by Onilab -- end

//Added by Onilab - Import desktop menu component and useWindowSize from Peregrine to detect mobile -- start
import {useWindowSize} from "@magento/peregrine";
import TopCategoryNav from "../Menu/topCategoryNav";
//Added by Onilab  -- end

// Modified by Onilab - Replace relative path with full path -- start
const SearchBar = React.lazy(() => import('@magento/venia-ui/lib/components/SearchBar'));
// Modified by Onilab -- end

const Header = props => {
   const {
       handleSearchTriggerClick,
       hasBeenOffline,
       isOnline,
       searchOpen,
       isPageLoading
   } = useHeader();
   
   const classes = mergeClasses(defaultClasses, props.classes);
   const rootClass = searchOpen ? classes.open : classes.closed;
   
   const searchBarFallback = (
       <div className={classes.searchFallback}>
           <div className={classes.input}>
               <div className={classes.loader} />
           </div>
       </div>
   );
   
   const searchBar = searchOpen ? (
       <Suspense fallback={searchBarFallback}>
           <Route>
               <SearchBar isOpen={searchOpen} />
           </Route>
       </Suspense>
   ) : null;
   
   const pageLoadingIndicator = isPageLoading ? (
       <PageLoadingIndicator />
   ) : null;
   
   //Added by Onilab - Header elements depends on screen size -- start
   const windowSize = useWindowSize();
   const isMobile = windowSize.innerWidth <= 960;
   
   const navTriggerMobile = isMobile ? (
       <div className={classes.primaryActions}>
           <NavTrigger />
       </div>
   ) : null;
   
   const categoryNavigation = !isMobile ? (
       <TopCategoryNav />
   ) : null;
   //Added by Onilab -- end
   
   return (
       <header className={rootClass}>
           <div className={classes.toolbar}>
               {/*Replaced by Onilab - Nav trigger obly on mobile-- start */}
               {navTriggerMobile}
               {/*Replaced by Onilab -- end */}
               {pageLoadingIndicator}
               <OnlineIndicator
                   hasBeenOffline={hasBeenOffline}
                   isOnline={isOnline}
               />
               <Link to={resourceUrl('/')}>
                   <Logo classes={{ logo: classes.logo }} />
               </Link>
               {/*Added by Onilab - Category navigation only on desktop -- start */}
               {categoryNavigation}
               {/*Added by Onilab -- end */}
               <div className={classes.secondaryActions}>
                   <SearchTrigger
                       active={searchOpen}
                       onClick={handleSearchTriggerClick}
                   />
                   <AccountTrigger />
                   <CartTrigger />
               </div>
           </div>
           {searchBar}
       </header>
   );
};

Header.propTypes = {
   classes: shape({
       closed: string,
       logo: string,
       open: string,
       primaryActions: string,
       secondaryActions: string,
       toolbar: string
   })
};

export default Header;
  • Start with the following:
import React, { Suspense } from 'react';
import { shape, string } from 'prop-types';

import Logo from '../Logo';
import { Link, resourceUrl, Route } from '@magento/venia-drivers';
  • Replace the relative path with the full path:
// Modified by Onilab - Replace relative path with full path -- start
import AccountTrigger from '@magento/venia-ui/lib/components/Header/accountTrigger';
import CartTrigger from '@magento/venia-ui/lib/components/Header/cartTrigger';
import NavTrigger from '@magento/venia-ui/lib/components/Header/navTrigger';
import SearchTrigger from '@magento/venia-ui/lib/components/Header/searchTrigger';
import OnlineIndicator from '@magento/venia-ui/lib/components/Header/onlineIndicator';
import { useHeader } from '@magento/peregrine/lib/talons/Header/useHeader';

import { mergeClasses } from '@magento/venia-ui/lib/classify';
import defaultClasses from '@magento/venia-ui/lib/components/Header/header.css';
import PageLoadingIndicator from '@magento/venia-ui/lib/components/PageLoadingIndicator';
// Modified by Onilab -- end
  • Import the desktop menu component and use WindowSize from Peregrine to detect mobile:
//Added by Onilab - Import desktop menu component and useWindowSize from Peregrine to detect mobile -- start
import {useWindowSize} from "@magento/peregrine";
import TopCategoryNav from "../Menu/topCategoryNav";
//Added by Onilab  -- end
  • Replace the relative path with the full path once more:
// Modified by Onilab - Replace relative path with full path -- start
const SearchBar = React.lazy(() => import('@magento/venia-ui/lib/components/SearchBar'));
// Modified by Onilab -- end
  • Continue by:
const Header = props => {
   const {
       handleSearchTriggerClick,
       hasBeenOffline,
       isOnline,
       searchOpen,
       isPageLoading
   } = useHeader();
   
   const classes = mergeClasses(defaultClasses, props.classes);
   const rootClass = searchOpen ? classes.open : classes.closed;
   const searchBarFallback = (
       <div className={classes.searchFallback}>
           <div className={classes.input}>
               <div className={classes.loader} />
           </div>
       </div>
   );
   const searchBar = searchOpen ? (
       <Suspense fallback={searchBarFallback}>
           <Route>
               <SearchBar isOpen={searchOpen} />
           </Route>
       </Suspense>
   ) : null;
   const pageLoadingIndicator = isPageLoading ? (
       <PageLoadingIndicator />
   ) : null;
  • Indicate that the header elements depend on the screen size:
//Added by Onilab - Header elements depends on screen size -- start
const windowSize = useWindowSize();
const isMobile = windowSize.innerWidth <= 960;

const navTriggerMobile = isMobile ? (
   <div className={classes.primaryActions}>
       <NavTrigger />
   </div>
) : null;

const categoryNavigation = !isMobile ? (
   <TopCategoryNav />
) : null;
//Added by Onilab -- end
  • Depending on the trigger (whether the current screen size is mobile), we display a specific menu, mobile or desktop:
return (
       <header className={rootClass}>
           <div className={classes.toolbar}>
               {/*Replaced by Onilab - Nav trigger obly on mobile-- start */}
               {navTriggerMobile}
               {/*Replaced by Onilab -- end */}
               {pageLoadingIndicator}
               <OnlineIndicator
                   hasBeenOffline={hasBeenOffline}
                   isOnline={isOnline}
               />
               <Link to={resourceUrl('/')}>
                   <Logo classes={{ logo: classes.logo }} />
               </Link>
               {/*Added by Onilab - Category navigation only on desktop -- start */}
               {categoryNavigation}
               {/*Added by Onilab -- end */}
               <div className={classes.secondaryActions}>
                   <SearchTrigger
                       active={searchOpen}
                       onClick={handleSearchTriggerClick}
                   />
                   <AccountTrigger />
                   <CartTrigger />
               </div>
           </div>
           {searchBar}
       </header>
   );
};

Header.propTypes = {
   classes: shape({
       closed: string,
       logo: string,
       open: string,
       primaryActions: string,
       secondaryActions: string,
       toolbar: string
   })
};

export default Header;

Step 4: Redesigning the Sign-in Area

In this step, we transfer the user sign-in and user authentication/registration menu to the pop-up format.

a) Install the library for modal windows

First, install the react-modal library, which we will need to display the components in pop-up windows:

yarn add react-modal

b) Create a modal window component

Make a simple modal window component that can easily be expanded and improved if necessary.

import React from "react";
import ReactModal from "react-modal";

ReactModal.setAppElement('#root');

const LoginModal = props => {

   const {isOpen, close} = props;
   
   const customStyles = {
       overlay: {
          zIndex: 999,
       },
       content : {
           top: '50%',
           left: '50%',
           right: 'auto',
           bottom: 'auto',
           marginRight: '-50%',
           transform: 'translate(-50%, -50%)'
       }
   };
   
   return (
       <ReactModal
           isOpen={isOpen}
           onRequestClose={close}
           style={customStyles}>
           {props.children}
       </ReactModal>
   );
}

export default LoginModal;

c) Change the component

Now we need to re-assign some of the default functionality of the PWA Studio once again. In our example, we take the AccountMenu component and redefine only the one file we need.

Copy this file from:

venia-create-app/node_modules/@magento/venia-ui/lib/components/AccountMenu/accountMenu.js

To:

venia-create-app/src/components/AccountMenu/accountMenu.js

d) Replace relative paths & implement the changes

Make changes in such a way that everything related to authorization and registration will work in the modal window. What’s for the authorized user, the sign-in should stay within the drop-down window (like it was previously before we made changes).

module.exports = componentOverride = {};

4. Building a PWA Using the Magento 2 PWA Studio: Backend Peculiarities

In this section of the blog post, we’d like to show how to create a Magento module that would return a product list shown within the slider.

Step 1: Create a module & the required files

Begin by creating a Magento 2 module. We’ll name it PWA_MyProductList. We need several files that make up the required minimum in Magento 2 to create modules.

  1. We need a registration.php file.
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
   'PWA_MyProductList',
   __DIR__
);
  1. Next, let’s create a module.xml file.
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="PWA_MyProductList" setup_version="1.0.0">
 <sequence>
  <module name="Magento_Catalog"/>
 </sequence>
</module>
</config>

Step 2: Create schema.graphqls file

Now it’s time to create a file containing the GraphQL schema for the request. This file should also indicate the resolver that’ll process the request.

type Query {
   customProductsList: [ ProductsBlock ] @resolver(class: "PWA\\MyProductList\\Model\\Resolver\\ProductsBlock")
}

type ProductsBlock {
   name: String
   url: String
   image: String
   id: Int
}

Step 3: Create a resolver

All that’s left at this point is making a resolver responsible for returning the necessary product list.

<?php
declare (strict_types = 1);

namespace PWA\MyProductList\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;

class ProductsBlock implements ResolverInterface
{
   const PRODUCTS_LIMIT = 10;
   
   /**
    * $productCollectionFactory
    *
    * @var ProductCollectionFactory
    */
   private $productCollectionFactory;
   
   /**
    * __construct
    *
    * @param ProductCollectionFactory $productCollectionFactory
    */
    
   public function __construct(
       ProductCollectionFactory $productCollectionFactory
   ) 
       $this->productCollectionFactory = $productCollectionFactory;
   }
   /**
    * @inheritdoc
    */
   public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
   {
       $result = [];
       $storeId = $context->getExtensionAttributes()
           ->getStore()
           ->getStoreId();
           
       $collection = $this->productCollectionFactory
           ->create()
           ->addStoreFilter($storeId)
           ->addFieldToFilter('customt_attribute', 'custom_value')
           ->setOrder('sort_order', 'ASC')
           ->setPageSize(self::PRODUCTS_LIMIT);
           
       foreach ($collection as $item) {
           $result[] = [
               'name' => $item->getName(),
               'url' => $item->getProductUrl(),
               'image' => $item->getImage(),
               'id' => $item->getId()
           ];
       }
       
       return $result;
   }
}

Step 4: Create a GraphQL query

As of now, the product list is returned by Magento 2. For the progressive web app we’re building, we need this to be handled by GraphQl instead. Therefore, create a file that’ll contain such a GraphQL query.

import { gql } from '@apollo/client';
export const PRODUCT_LIST_BLOCK = gql`
   query CustomProductsList {
       productsBlock {
           id
           name
           url
           image
       }
   }
`;
export default PRODUCT_LIST_BLOCK;

Step 5: Create a Magento query

The final step is creating a Magento 2 query, which allows us to get a product list in the slider.

import React from 'react';
import Swiper from 'swiper/react';

import Loader from "src/Components/General/Loader";
import Error from "src/Components/General/Error";
import useProductList from "src/talons/RootComponents/Homepage/useProductList";

const CustomProductsBlock = () => {
   const {loading, error, productsList} = useProductList();
   
   if (error) {
       return <Error />;
   }
   
   if (loading) {
       return <Loader />;
   }
   
   const params = {
   };
   
   return (
       <>
           <div className={`pageWrapper`}>
               <div className={`containerWrapper`}>
                   <Swiper {...params}>
                       <>
                           {productsList.map(product => {
                               return <div key={ product.id } className={ 'product-slider' }>
                                   <Link to={ product.url } className={ 'product-image' }>
                                       <img src={product.image} />
                                   </Link>
                                   <Link to={ product.url } className={ 'product-link' }>
                                       { product.name }
                                   </Link>
                               </div>
                           })}
                       </>
                   </Swiper>
               </div>
           </div>
       </>
   );
};
export default CustomProductsBlock;

Plus, as shown in the file below, create a talon for obtaining the product list:

import { useQuery } from '@apollo/client';
import { useEffect, useState } from 'react';
import PRODUCT_LIST_BLOCK from "src/Queries/GetProductList";

const useProductList = () => {
   const [productsList, setProductsList] = useState(null);
   
   const {loading, error, data} = useQuery(PRODUCT_LIST_BLOCK, {
       fetchPolicy: 'cache-and-network',
       nextFetchPolicy: 'cache-first',
   });
   
   useEffect(() => {
       if (data) {
           setProductsList(data.customProductsList);
       }
   }, [data]);
   
   return {
       error,
       loading,
       productsList
   };
};

export default useProductList;

5. PWA Studio Tips from the Pros

Now that it’s clear how to build a PWA with the use of the Magento PWA Studio, let’s go over some of the recommendations given by the pros. The following block will give more information on how to use the instrument and which mistakes to avoid.

1. Talons or Hooks

The first point in the overview regards what talons are in the Magento 2 PWA Studio and how they differ from custom React hooks.

Put simply, React components have sections for logic and visualization. In the Magento PWA Studio, these components are split into Peregrine Talons (used for logic) and Venia visual components (needed for content visualization). In their turn, Peregrine Talons are a kind of React hooks that provide standard functionality and logic for Venia UI components. Unlike Peregrine Talons, Peregrine hooks can be reused.

You can learn more about talons and hooks .

2. Possible Launching Issues with Windows

When using Windows, you may encounter additional launching problems. In an ideal scenario, you should avoid using the Windows object since it can cause additional problems during the launch. You can take a look at such a .

3. Reusable Constants

It is possible to implement new environment variables, yet there is no proper structure that would allow reusing regular constants in the project. There’s a workaround solution for this. For instance, you can make a “Constants” folder (like we have done in our step-by-step Magento PWA Studio tutorial), define them in it, and then reuse them. The official Magento team brings up one more that you can also check out.

4. Fragmenting GraphQL Queries

The Magento PWA Studio does not use (or hardly ever uses) fragments for . But you can break large queries into chunks and then reuse them a large number of times.

To give an example of when this is applicable, you can use this in case you need to add a field to the requests of all types of product cards. This way, you’ll just add this field to a single fragment.

Make sure to apply this advice carefully when you work on your Magento 2 PWA Studio setup. Do your best to request only the necessary data minimum in your GraphQL queries. Note that if you apply this method without due thought, this may have consequences. For instance, in the case of listings, if you add the description field to the product, you can greatly increase the duration of the , the load time, and the space occupied in the cache.

5. Caching

When handling the caching matter, do not forget about the localStorage limitations. Otherwise, an overflow may occur.

What is more, the caching of some objects must be configured separately for the Apollo cache.

Plus, when you build the webpack, it is possible to cache configurations and the URLs of your resolvers within the environment variables or in the template.

6. Overriding Standard Components

Another thing you should keep in mind is that when you override standard components, there may be extra styles. In turn, this can increase the total weight of the files, which isn’t good. This is why it’s worth investing time in cutting out excessive code blocks with styles.

Final Thoughts

We’ve shown several examples of how to change the web app built with Magento PWA Studio when deploying a progressive web application on Magento 2. If your UX is very different from what Magento PWA Studio offers, then you’ll have to redefine many components this way. As a result, it may turn out that the Magento PWA Studio will be completely redefined, and essentially, you won’t need it. If this is the case, it’ll take a couple of days to tweak it, simplify the code, and reduce the weight of the application.

The theme general design is often accused of being boring, unsightly, and shady, and you may not find the desired typeface or encounter another specific PWA Studio problem. But in any case, the PWA Studio is a great start for those building a progressive web app for the first time. If you need help with , don't hesitate to contact us and !

Related Articles

How to Create a Scratch Org from a Shape Org in Salesforce
Max Matin
Max Matin, Certified Community Cloud professional

How to Create a Scratch Org from a Shape Org in Salesforce

7 min
Jan 15, 2021
Magento 2.3 PWA Studio: All Hail the Progressive Web App Revolution!
Alex Husar
Alex Husar, Onilab CTO

Magento 2.3 PWA Studio: All Hail the Progressive Web App Revolution!

13 min
Dec 10, 2018
Magento 2 Speed Optimization: 32 Effective Fixes (Updated 2024)
Alex Husar
Alex Husar, Onilab CTO

Magento 2 Speed Optimization: 32 Effective Fixes (Updated 2024)

21 min
Jul 6, 2023
Magento Open Source vs. Commerce: What to Choose in 2024
Alex Husar
Alex Husar, Onilab CTO

Magento Open Source vs. Commerce: What to Choose in 2024

7 min
Sep 11, 2023

Magento PWA Studio: FAQ

What Is PWA Studio in Magento 2?

The Magento PWA Studio is a toolkit, among other PWA themes like Vue Storefront, that enables PWA integration into Magento stores. It’s one of the ways to build a PWA storefront directly from the out-of-the-box developer tools, which is faster than coding from scratch. 

This solution lets you construct an online store with less coding and optimize its components. It provides various libraries and capabilities to build systems and frameworks that meet the Magento principle of extensibility.

What Are the Technical Components of a Magento PWA?

A Magento PWA relies on several technical components, including:

  • A web app manifest (a JSON file responsible for how the app will behave and containing the information, such as the name, author, icons, links, etc.);
  • Transport Layer Security (the standard that enables PWAs to maintain security during data transfer, involves installing an SSL certificate on a server, and calls for HTTPS as the protocol for the PWA);
  • Application shell architecture (one of the ways to make the web app load faster as users can see the page skeleton while the content appears on the screen);
  • Service worker (a JavaScript file that makes the main PWA features possible, including the offline mode to operate without the Internet, background sync, and push notifications like in native mobile apps).

Is a PWA a Worthwhile Investment?

As mobile commerce is on the rise, providing a seamless shopping experience on the mobile phone is key. A PWA can be a great solution for this. It supports push notifications, loads quickly, doesn't require downloading from app stores, and lets you save money on developing separate native apps for Android, iOS, and Windows. It can be added to the Home Screen as a shortcut and upgraded automatically.

Various business owners, from travel agencies to Pinterest and Tinder, have already released PWAs. They either work instead of native apps or together with them as an optimized website version to meet the expectations of mobile visitors. No wonder why many Magento stores also follow suit and start their own PWA storefront project. All of the PWA features mentioned above make it a cost-effective solution for different businesses.

Let’s stay in touch

Subscribe to our newsletter to receive the latest news and updates.