— 6 min read
Edit post on GitHubThis blog post covers two areas. The first part is more from the business side of view. In the second part, we will cover how you can build a solid project with PWA Studio.
As with all software projects, it should start with the why. What can a customer or a developer expect from a progressive web app (PWA) Project?
In my experience, when new technology appears, stakeholders do not ask about any added business values or requirements first. But often jump on the hype train without a good reason. In my opinion, a better approach could be using Why, How, What (golden circle) before starting a Project with headless technologies. The why should be very customer-focused, but also do not forget about your organization strategies?
Why would we like to build the next project as a (PWA)?
How PWA improves the customer experience?
What is the advantage of using this new technology?
What is the technical trade-off in building a Headless frontend?
What are the target group of my PWA Project?
I think that headless is the future, especially for large e-commerce projects. Is the separation of frontend and backend beneficial to tame the complexity? A good Content Commerce strategy based on Adobe Experience Manager get more and more requested nowadays. In the future, I expected more seamless integration from all Adobe products in a headless storefront. That was one of the reasons why I got so involved with PWA. Now is an excellent time to gain the first experience in real projects with the new PWA technology.
After some significant technical and Bussines strategies meetings, you decided to start building a storefront based on PWAStudio. I know there many more solutions for Magento (Adobe Commerce) to build a Storefront.In my experience select, a product not developed by Adobe brings your Project, not an ideal situation in terms of support and maintainability. What I like significantly about PWA Studio is the extensibility of APIs.
They are not so common in the React environment but are extremely helpful in developing your own storefront experience. Before starting a project, you should ensure your development team knows about React, HTML, CSS, Webpack. They are a used foundation.
Summary Blog post: https://marcin-kwiatkowski.com/how-to-extend-pwa-studio-with-new-features
In this tutorial, we are going to extend the Main and create our TopBar component as the first element to our site.
In my experience, the Project scaffolding command developed by Adobe is an excellent way to start a project. Early adopters of the PWA Studio project forked, and the Core Team recommends not using this practice. With scaffolding, we should have the opportunity to build our own PWA Theme on top of Magento 2. The complete documentation of https://magento.github.io/pwa-studio/pwa-buildpack/scaffolding/
But it would help if you had some tweaks to get better maintainability.
Command to create a new Project:
yarn create @magento/pwa
It should look like:
If you work on a project with only the local-intercept.js, this file gets very fast confusing. I create a yarn extension that you install and get a better maintainable project. It allows you to .targetables.js
in any competent which help to split the huge local-intercept.js
in a file per component also it allows css overwrites and extension.
yarn add @larsroettig/component-targetables
Change your Project local-intercept.js
file to:
const {
componentTargetablesDefinitions,
} = require('@larsroettig/component-targetables');
function localIntercept(targets) {
const { Targetables } = require('@magento/pwa-buildpack');
const targetables = Targetables.using(targets);
const extendLocalIntercept = new ExtendLocalIntercept(targetables);
extendLocalIntercept.allowCustomTargetables();
extendLocalIntercept.allowCssOverwrites();
}
Lets add a FreeGift before the header
import React from 'react';
import { useStyle } from '@magento/venia-ui/lib/classify';
import { FormattedMessage, useIntl } from 'react-intl';
import { BrowserPersistence } from '@magento/peregrine/lib/util';
import defaultClasses from './freeGift.css';
const storage = new BrowserPersistence();
const FreeGift = props => {
const classes = useStyle(defaultClasses, props.classes);
const { locale } = useIntl();
const value = 20; // schould be fetch via graphql
// fetchs the current currency from storage price compont
const currencyCode = storage.getItem('store_view_currency') || 'USD';
// sample to format a number price comp cloud be used here instad
const amount = new Intl.NumberFormat(locale, {
style: 'currency',
currency: currencyCode
}).format(value);
return (
<div className={classes.root}>
<span>
<FormattedMessage
id="freeGift" // Description should be a string literal
defaultMessage="Free Gift if you order for more then <b>{amount}</b>" // amount should be a string literal
values={{
amount,
// replace <b> tag via span with blod styling
b: chunks => (
<span className={classes.msg}>{chunks}</span>
)
}}
/>
</span>
</div>
);
};
export default FreeGift;
.root {
background-color: #baa683;
text-align: center;
padding: 1rem 0;
color: rgb(var(--venia-global-color-gray-50));
}
.msg{
font-weight: var(--venia-global-fontWeight-bold);
}
In our case we required to target node_modules/@magento/venia-ui/lib/components/Main/main.js
todo that we need to create file under src/components/Main/main.targetables.js
/**
* @param {MainComponent} MainComponent
* @see @magento/venia-ui/lib/components/Main/main.js
*/
const interceptComponent = MainComponent => {
// Adds the import to our main component
MainComponent.addImport(
"FreeGift from 'src/custom/components/FreeGift/freeGift.js'"
);
// Add our FreeGift component before <Header />
MainComponent.insertBeforeJSX('Header', '<FreeGift />');
};
exports.interceptComponent = interceptComponent;
** The Result in the core after transform**
import FreeGift from 'src/custom/components/FreeGift/freeGift.js'
const Main = props => {
const { children, isMasked } = props;
const classes = useStyle(defaultClasses, props.classes);
const rootClass = isMasked ? classes.root_masked : classes.root;
const pageClass = isMasked ? classes.page_masked : classes.page;
useScrollLock(isMasked);
return (
<main className={rootClass}>
<FreeGiftMessage />
<Header />
<div className={pageClass}>{children}</div>
<Footer />
</main>
);
};
If you learn more about the library pls visted the documentation.
SEO
For JavaScript applications, it is essential to use Server-Side Rendering (SSR). The benefit of using SSR is
the website content can crawl from crawlers that don't execute JavaScript code. I recommend using tools like prerender.io or seosnap.io.
When you use SSR to mind those errors, the request still can returns a 200 status code.
You don't want these to cache by Rendertron or Seosnap your need to set <meta name="render:status_code" content="404" />
at the appropriate places.
Very nice blog post over Server-Side vs Client-Side Rendering and Changing SEO Practices.
Lars Roettig is a Senior Software Engineer at TechDivision GmbH. digital agency focused on Adobe Commerce and modern web development. My personal goal is to teach you how to write stable software with quality.
Learn more about Lars