Best Practices
Important note
Make sure to read the accessibility best practices as well.
console.log
Remove all
console.log()
before publishing your store to production.
Images
- Always use ResponsiveImage.
- Use
sizes
to help browsers load the image in a proper size. - Set the
width
andheight
to the image to avoid Cumulative Layout Shift (CLS). Another resource here.- Images coming from variables inside Shogun Frontend have
width
andheight
sent in the media object. See example below:
- Images coming from variables inside Shogun Frontend have
import React from 'react'
import ResponsiveImage from 'frontend-ui/ResponsiveImage'
const SomeComponent = ({ image }) => (
<ResponsiveImage
src={image.src}
alt="A black t-shirt with a black Shogun logo on it. The t-shirt is on a red surface."
sizes="(max-width: 767px) 80vw, 40vw"
width={image.width}
height={image.height}
/>
)
export default SomeComponent
- If the image is within the viewport, set
loading="eager"
.
Server side vs. client side
PWAs renders your store on the server side, hence the name SSR (Server Side Rendering). This technique allows your store to be blazing fast, but, mind your code. If you need to use some code that relies on client side APIs, such as window
and/or document
, do so inside of an useEffect
hook:
import React from 'react'
const MyComponent = () => {
React.useEffect(() => {
const someElement = document.querySelector('.some-class')
})
return <p>My component.</p>
}
export default MyComponent
Detecting viewport width and height
Prefer a CSS approach to detect the width
and height
of a page. A JS approach such as the useWindowSize
hook will cause undesired results, since your store is rendered on the server side (SSR).
❌ Don't do this:
function Header() {
const { width } = useWindowSize()
const isMobile = width < 500
return isMobile ? <MobileHeader /> : <DesktopHeader />
}
✅ Do this instead:
import styles from "./styles.module.css"
function Header() {
return (
<>
<MobileHeader className={styles.mobile} />
<DesktopHeader className={styles.desktop} />
</>
);
}
.mobile {
display: block;
}
.desktop {
display: none;
}
@media (min-width: 500px) {
.mobile {
display: none;
}
.desktop {
display: block;
}
}
Guard clauses
When accessing object properties, make sure the parent property exists. For example, when mapping through an array of images, check if the array empty before mapping through its item.
import React from 'react'
import ResponsiveImage from 'frontend-ui/ResponsiveImage'
const ProductBox = ({ product }) => {
const { media } = product || {}
// do not render the component
// if media array is empty
if (media.length === 0) return
return (
<section className="ProductBox">
<h1>ProductBox</h1>
{media.map(img => (
<div key={img.id}>
<ResponsiveImage
src={img.src}
alt=""
sizes="(max-width: 767px) 80vw, 40vw"
width={img.width}
height={img.height}
/>
</div>
))}
</section>
)
}
export default ProductBox
CSS classes
For conditional classes, we recommend the classnames npm package. Contact support to install it into your store if needed.
Naming conventions
Below are some naming conventions recommendations. It's up to you and your team decide a naming convention, and above all, consistency is the key.
- We recommend using PascalCase for naming your components and sections.
- Consider using SUIT CSS for CSS classes
- Prefer descriptive variables names like
productImage
instead ofimg
(unless the variable context allows for clarity). - VariablesVariables - In the context of Shogun Frontend, variables are the props passed to the Sections. These are defined in the right sidebar in the IDE. These variables will be available when editing one of your pages, allowing you to map them to a field in the CMS or manually enter the values directly in the Experience Manager sidebar. names must follow the camelCase convention.
Section content variables
When adding content variables to a Section, make sure to select only the data fields that the Section needs. This will help to ensure that Shogun Frontend pages are loaded and displaying critical content with sub second speed.
Example for Navigation section that will display each collection by the Name attribute and include the slug/url to redirect to the correct collection page.


- To avoid increase in the store's build and publish process, double check if all the variables selected in the IDEIDE - Integrated Development Environment. for a section are actually in use within the code.
Fonts
- Inline external fonts to avoid resource chain.
- Apply
font-display: swap
to any externalfont-face
rule. - If using Google Fonts, make sure your font url has
&display=swap
on it.
Buttons
- A button should be used to trigger an action, for example: submit a form. A link is not a button.
- Use the
<button>
tag instead of<div className="button">
.
Links
Instead of using a normal a
tag, use Shogun Frontend Link.
For external links add:
target="_blank"
to open in a new tab.rel="noreferrer noopener"
to prevent security vulnerabilities.
Videos
Use the Video Component to lazy load videos by setting loading="lazy"
. If the video is on the viewport, use loading="eager"
.
Iframes
When possible, lazy load iframes with loading="lazy"
.
Performance
- Load third-party scripts integrations from the
App
component. - Preconnect common used hosts
https://fonts.googleapis.com
if using Google fonts
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
Reusable Functions/React Hooks
It’s expected that you will have functions or custom React Hooks that needed to be reused in other parts of your codebase.
To accomplish this, we suggest creating a Component for each grouping in a manner that’s clear to its contents such as ProductPageHooks
or GlobalUtilFunctions
with each function being exported:
import React from 'react'
export const someFunction = () => {
...
}
export const someFunction2 = () => {
...
}
You can then utilize these functions anywhere in other Components or Sections via imports:
import { someFunction, someFunction2 } from "Components/GlobalUtilFunctions";
Updated 18 days ago