Build and deploy e-commerce progressive web apps (PWAs) in record time.
Full Guides, API Documentation, and Examples
Example Site Built with React Storefront
You can create a local copy of this site using create-react-storefront
to use as a starting point for your own site:
npm install -g create-react-storefront
create-react-storefront my-site
React Storefront is licensed under the Apache 2.0 License.
To contribute to react-storefront:
master
yarn test
First, clone the repo and run yarn to install dependencies
yarn
To use your local copy of react-storefront when developing apps, in your clone of this repo, run:
yarn link:all
To automatically transpile your code when you make changes, run:
yarn watch
Then, in your app's root directory run:
npm run dev:link
You need to rerun the above every time you run npm install
because npm overwrites linked modules when new modules are installed.
prettier-vscode
can be installed using the extension sidebar.
To format on save, just update your editor.formatOnSave
setting.
For other editors, https://prettier.io/docs/en/editors.html
To publish a release, run:
yarn release
x-xdn-route
header.tabsProps
to TabPanel
.aria-label
attribute to Drawer
and ImageSwitcher
.infinite
quick turnaround bugCarousel
to ImageSwitcher
ReactImageMagnify
to carousels, to allow images to be magnified on hover on PDPsprevButtonProps
and nextButtonProps
not being passed to Carousel
render.prevButtonProps
and nextButtonProps
to Carousel
.poster
attribute for videos in <ImageSwitcher>
infinite
, slideRenderer
, slidesToShow
, and swipeableViewsProps
propswidth
prop to AmpCarousel component_rsf_analytics=0
query parameterTrack
property to delay firing analytics eventsonClose
prop of Menu
is not called when the menu is closed.inputProps
prop to SearchDrawer
SameSite=None
attribute in cookie stringlazyOffset
on the <Image />
component when horizontal scrollingapplication/json
to be not be valid JSON. Requests of this type that contain unparsable JSON will now log a warning message rather than force the request to fail entirely.drawerProps
to SearchDrawer
type
prop to AmpCarousel
fromOrigin
and redirectTo
route handlers when running A/B tests. This is done by moving the outer edge routing logic to the moov backend in oem.jsonreact-storefront-edge
:npm i --save-dev [email protected]^1.0.0
config/webpack/webpack.prod.edge.js
to:const edge = require('react-storefront-edge/webpack')
const path = require('path')
module.exports = edge(path.join(__dirname, '..', '..'), {
router: 'src/routes.js'
})
thumbnailImageProps
to ImageSwitcher
imageService
for information about transitioningfetchWithCookies
will no longer URI encode cookies when running in a handler with a custom cache key that splits the cache by cookie. URI encoding cookies is not standard and breaks some back ends.SocialShareButtons
typesRouter.watch
on page load anymore.zoomSrc
to MediaTypeModel
. Use zoomSrc
to specify a separate high-resolution URL to display when the user activates the pinch/zoom modal on the PDP.Tab
component from NavTab
sendPreloadHeaders
to false
when calling react-storefront-moov-xdn/index
from scripts/index
in your project.Example:
// scripts/index.js
console.error = console.warn = console.log
module.exports = function() {
require('../src/analytics')
const index = require('react-storefront-moov-xdn').default
const { transformAmpHtml } = require('react-storefront-extensions/amp')
const errorReporter = require('../src/errorReporter').default
index({
theme: require('../src/theme').default,
model: require('../src/AppModel').default,
App: require('../src/App').default,
router: require('../src/routes').default,
blob: env.blob || require('../src/blob.dev'),
transform: transformAmpHtml,
errorReporter,
sendPreloadHeaders: false
})
}
Link
elements with a to
prop that points to a fromOrigin
route do not work.fromOrigin
and redirectTo
in your router's fallback
handler.Example:
cache({
edge: {
maxAgeSeconds: 1000,
key: createCustomCacheKey()
.addHeader('x-moov-xdn-device')
.addHeader('country', header => {
header.partition('na').byPattern('us|ca')
header.partition('eur').byPattern('de|fr|ee')
})
}
})
utils/batchPromises
concurrent execution.fetch
calls made in handler functions now forward the x-forwarded-for
request header automatically.
fromOrigin
in local development. In local development fromOrigin
simply uses proxyUpstream()
with no arguments.router.fallback(fromOrigin())
and router.fallback(redirectTo(url))
client
and server
props to <Lazy/>
, giving you the option to fully render a page on the server while making some components lazy during client side navigation.additionalDelay
bug which caused hydration before loadfromOrigin
handler.withPersonalization
will no longer fire the supplied callback unless the parent page component is visible.additionalDelay
option to launch client to further delay hydration after page load.load
event fires to improve largest image render times.// src/client.js
import App from './App'
import launchClient from 'react-storefront/launchClient'
import model from './AppModel'
import router from './routes'
import theme from './theme'
launchClient({
App,
router,
theme,
model,
delayHydrationUntilPageLoad: true
})
metadata
object to all analytics events with the following keys:metadata: {
title: document.title,
pathname: location.pathname,
search: location.search,
uri: location.pathname + location.search,
referrer: document.referrer
}
Response
's handling of cookies. Now able to set multiple cookies.Carousel
component that supports AMPredirected
flag to response context for downstream event handlerstitle
, description
and canonicalUrl
of AppModel
set to null.TabPanel
amp initial stateAccordion
component which only allows one child ExpandableSection
to be open at a time.ProductColors
and ProductThumbnail
components to support dynamic color swatches within a PLP page.AmpForm
that caused errors when injected into non-PWA pages.DrawerButton
component for creating drawers with custom content with support for anchoring from any sideLazy
no longer requires a key
prop to automatically unmount it's children when the URL changes.visibilitySensorProps
to Lazy
.Lazy
are now shown in AMP.<title>
tag when app.title
is null
renderLeafHeader
and renderLeafFooter
to Menu
. These allow developers to customize the header and footer sections for non-root menu cards.linkProps
prop to HeaderLogo
for adding props into Link
component.Image
to leave src
in place when the image fails to load and notFoundSrc
is not defined.<Image />
components with lazy
parameter and no width are never rendered if they are initially above the fold.throttleClick
prop to AddToCartButton
with a default of 250 milliseconds to help prevent users from adding a product to their cart multiple times by accident.SearchDrawer
. In order to make the search drawer work in AMP:
SearchButton
component in your app header to open the search drawer.search/suggest-handler.js
, add thumbnails: (true|false)
to each item in groups. Set to true
when items in the group have a thumbnail, otherwise false
.renderers/render
method is now correctly marked async
.ImageSwitcher
now uses the product name for the alt
prop on any images or thumbnails that do not have one.Lazy
component are never rendered if they are initially above the fold.headers.get(name)
not being defined.amp-analytics
tags by implementing getAmpAnalyticsData()
on analytics targets.Headers
implementation to support spreading, deleting, direct access of properties.ImageSwitcher
can now display videos.Video
component which supports displaying videos in AMP.fromOrigin(originName)
handler type that allows you to proxy an origin from the edge.redirectTo(path)
handler type that allows you redirect the request at the edge.AmpImageSwitcher
.Server
cache({ edge })
is present on a route and has a key
property, the cookies in that key will be forwarded on any fetch
calls sent upstream.react-storefront/requestContext
for request scoped state storage access@withAmp
is not present on the main page component or if react-storefront-extensions/transformAmpHtml
is not used.rsf_disable_analytics
to true
. The default smoke test now disables analytics so that smoke test runs aren't counted as real user sessions.SearchPopup
componentcache/clearClientCache
no longer requires the service worker to be installed in order to clear the cache. This is now done entirely by the main browser thread.react-storefront-puppeteer
package to help developers write puppeteer tests for React Storefront apps and schedule smoke testing with Moovweb Control Center.BackToTop
component for scrolling to top of page.authorization
header sent from the browser will be forwarded to fetch calls made from the server when basic auth is enabled.react-storefront/fetch
for backwards compatibility.react-storefront/fetch
for backwards compatibility.Menu
would not reset the user taps on an item to drill down.clearClientCache()
from react-storefront/cache
selected
class.fetch()
now relays the user-agent
header from the browser if one is not explicitly provided.server
option in cache()
route handlers has been renamed edge
. Usage of server
is still supported, but will result in a deprecation warning.cache()
handler's new server.key
property and react-storefront/router/createCustomCacheKey
.searchSubmitted
and searchLinkClicked
to SearchDrawerServer
now cleans and minifies server side rendered CSShydrate
now lives in utils. Used internally and for RSF components in adapt pages.require("history/createBrowserHistory")
.Server
now properly handle the case when rendering an error fails..
diff)SearchResultsModelBase
nows updates facetGroups
if defined in a search results response..
diff)Filter
issue where all group items were being rendered even when not expandedlodash
dependency to fix a vulnerabilityhideLast
prop to to the Breadcrumbs
component.serviceWorker: false
to launchClient
.resetSelectionWhenImagesChange
prop to ImageSwitcher
.PageLink
now merges the state
prop with state automatically created from the model
prop.optimize
prop on Image
component which allows you to optimize images for mobile devices using Moovweb's CDN.CmsSlot
ShowMore
button so that it matches the button heightCmsSlot
.history
cannot be injected from app scope.Lazy
component for late loading components with a simple wrapperCmsSlot
now lazy loads images with data-rsf-lazy
attributelazyItemsURL
in MenuItemModel
instances.itemRenderer
and itemContentRenderer
props.ShowMore
button.labelComponent
prop to BackNav
, which allows you to override the default HTML element used to render the label.serviceWorker.cache(path)
even if the path matches a route that doesn't have a cache handler.Please use require("history").createMemoryHistory instead of require("history/createMemoryHistory")
SearchDrawer
now handles a null value for app.search.results
gracefully.createSubmitURL
prop to SearchDrawer
.initialGroups
to display in the search drawer when the search field is blank.imageProps
prop to ImageSwitcher
useContext
hook via the new react-storefront/AppContext
.withPersonalization
HOC and usePersonalization
hook for late loading personalized data.initialContent
prop to SearchDrawer
that determines the content to display when the search field is blank.ExpandableSection
icons were showing wrong icon when using defaultExpanded
.renderers/render
which caused injection of PWA components into proxied pages to fail. This was a regression introduced in 6.26.0delayUntilInteractive
prop to AnalyticsProvider
that delays loading analytics scripts until the app is fully interactive. This helps ensure the best TTI and user experience.optimizeImages
util function for use in handlersoptimization
options in the client build config.OPEN_BROWSER
environment variable to control
whether or not the browser opens after starting in development modeerrorReporter
config on both react-storefront/launchClient
and react-storefront-moov-xdn/index
.applySearch
function stringifies params. For example, router.applySearch({ colors: ['red', 'green'] }, { arrayFormat: 'brackets' })
fromServer
during client-side navigation is cached.Menu
component now looks the same when rendering in AMP and React.ImageSwitcher
would not reset its selectedIndex
after switching products.sortProps
to SortButton
, which allows your to pass props to the underlying Sort
component.Added serveSSRFromCache
option to the client webpack build. Set to true
to allow the sevice worker to serve from the cache when a user initially lands on your app. Defaults to false
.
Fixed the padding of the close button in the UpdateNotification
component.
MenuIcon
with better animation. Note: MenuIcon's OpenIcon
and CloseIcon
props have been removed.redirected
and url
properties on the Response
object. See https://developer.mozilla.org/en-US/docs/Web/API/Response#Properties.new Router().get(
'/p/:1',
cache({
server: {
surrogateKey: (params, request) => {
return 'product'
}
}
}),
fromServer('./path/to/handler')
)
trackSelected
prop to Menu
. Set to true
to indicate the item corresponding to the current pageMenu
by eliminating excessive rendering.<a data-rsf-power-link="on" href="https://my.domain.com">Visit My Store</a>
and have the link prefetched and cached so that navigation is instant. Just add this to the site containing the link:<script src="http://my.domain.com/.powerlinks.js" defer />
cache()
route handler with response.set('cache-control', '...')
.environment
module with isClient
and isServer
functions that allows you to detect whether your code is running on the client or the server.set
, get
, status
, cookie
, and redirect
methods on the client.AppBar
component now displays "Your device lost its internet connection" when offline. This message is configurable via AppBar's offlineWarning
prop.Offline
component to be displayed as the main body of the app when the user attempts to navigate to a page that isn't cached when offline.appShell
configuration method to Router
. Configure the appShell with a fromServer
handler that returns global data to display in the app shell when the user attempts to load the site while offline.To add offline support to your app, upgrade to 6.12.0, then:
appShell
configuration to your router definition:// src/routes.js
new Router()
// ...
.appShell(
// returns only the global data needed to build the app-shell for offline support
fromServer('./app-shell/app-shell-handler')
)
Offline
component to your Pages
element in App.js
.// src/App.js
import Offline from 'react-storefront/Offline'
// then in the render method...
class App extends Component {
render() {
return (
<Pages
components={universal => ({
// ...
Offline
})}
/>
)
}
}
history.replace
fails due to the state object being too large. This was happening on Firefox for apps with large state trees as Firefox imposes a limit of 640kB on the state object. When history.replace fails, history.state will simply be cleared out and the app
will get the state from the network if the user navigates back or forward.onImpression
from Link
. We decided this logic was better handled in CommerceAnalyticsTarget
in react-storefront-extensions
.AnalyticsProvider
now automatically calls setHistory
for all targets.Link
where onImpression
would not fire if the user returns to a page using the back or forward buttons.Link
where onImpression
would not fire unless prefetch="visible"
was also presentacceptInvalidCerts
option to fetch
transform
passed into react-storefront-moov-xdn/index
can now be asynchronous. The allows react-storefront-extensions/transformAmpHtml
to fetch heights and widths for images when rendering AMP.utils/batchPromises
for running batches of concurrent promises.onImpression
prop to Link
to help with tracking product impressions using Track
.currencyCode
to ProductModelBase
ImageSwitcher
component when rendered in AMP without thumbnails.AmpImageSwitcher
due to a bug in amp-carousel
when rendering in a div with display: flex
. https://github.com/ampproject/amphtml/issues/14519
ExpandableSection
in AMP.CmsSlot
now spreads props to the underlying span
. This fixes an issue where <Track>
would not fire events when a CmsSlot
was the child element.AnalyticsProvider
accidentally removed in 6.8.0className
to MenuItemModel
. This allows you to add CSS class names to individual items in the Menu.Filter
and FilterButton
:
LoadMask
into Filter
's facetGroups
block when model is loading<button>
instead <a>
w/o href
attributecontent-type: text/plain
in post bodies.ImageSwitcher
's thumbs
class is not applied when rendering AMP.Rating
minimumTextLength
to SearchModelBase
AmpModal
component based on <amp-lightbox>
.AnalyticsProvider
for loading analytics on mountDrawer
component on iOS <= 10Menu
now renders children so you can add custom controls.name
prop to QuantitySelector
to make it easier to submit the value when rendering AMP.fetch
now supports the redirect
option with values "follow"
, "error"
, and "manual"
.x-rsf-routes
header to get available route information.searchButtonVariant
and showClearButton
props to SearchDrawer
to give you greater control over the behavior of the search input.proxyUpstream
handler runs on the client.notFoundSrc
prop to Image
component which will be used in case the primary image source fails to loadfetch
now supports inflating responses with content-encoding: gzip
<Track>
now allows you to map triggers to events. For example: <Track trigger={{ onVisible: 'productShown', onClick: 'productClicked' }}>
<Link>
now has a onVisible
prop that you can use to be notified when a link is scrolled into the viewport.withGlobalState(request, callback, localState)
now passes request
to the callback
.analytics.fire('eventName', data)
instead of the proxied methods like analytics.eventName(data)
...
diff)Filter
and FilterButton
:
LoadMask
into Filter
's facetGroups
block when model is loading<button>
instead <a>
w/o href
attribute..
diff)Drawer
component on iOS <= 10AnalyticsProvider
for loading analytics on mount..
diff)AmpModal
component based on <amp-lightbox>
.notFoundSrc
prop to ImageSwitcher
and handle missing images before the app mounts.searchButtonVariant
and showClearButton
props to SearchDrawer
to give you greater control over the behavior of the search input.proxyUpstream
handler runs on the client.notFoundSrc
prop to Image
component which will be used in case the primary image source fails to loadanalytics.fire('eventName', data)
instead of the proxied methods like analytics.eventName(data)
.state
field to BreadcrumbModel
so that state can be passed to skeletons when the user clicks on a breadcrumb.ANALYZER_MODE
env variable...
diff)cookie
helper method to Response
<iframe>
with <amp-iframe>
when rendering AMP.<iframe>
with <amp-youtube>
when rendering AMP.yarn link:all
during CI builds to ensure that linking will work properly.variant="drawer|menu"
to FilterButton
. The default is "drawer
"...
diff)envVariables
to webpack server optionsitemRenderer
prop to Menu
ANALYZE=true
to see client build stats in your browser.<AppBar menuAlign="right"/>
and <Menu align="right"/>
.<AppBar menuIconProps={{ label: false }}/>
onChange
prop.app.loading
to true
in PageLink
to eliminate a reconciliation cycle...
diff)Prefetching now ramps up over the course of 25 minutes by default to ease the load on servers after clearing the cache during deployment
Removes some assets from the precache manifest that don't need to be prefetched.
response.set('content-type', contentType)
.You can now override <meta>
tags using react-helmet
.
Now throws an error in development when a cache handler runs during non-GET request
Removes set-cookie headers when route has a cache handler with server maxAgeSeconds > 0.
Automatically caches all proxied images and fonts for a day
response.json()
helper method for sending JSON dataX-Frame-Options: SAMEORIGIN
response header by default.response.redirect(url, status)
no longer requires you to call response.send() afterwards.<Image lazy/>
and <Link prefetch="visible"/>
elements would eager fetch when hidden by upgrading react-visibility-sensor.anchorProps
to LinkTrack
ProductModelBase.basePrice
ProductModelBase.price
is now a view that returns the price
of the selected size or, if not present, the basePrice
.ButtonSelector
can now display a CSS color code instead of an image via the new color
field on OptionModelBase
ButtonSelector
can now be configured to display a strike through when disabled by setting strikeThroughDisabled
. The angle can be controlled via strikeThroughAngle
.track
handler module has been removed<Track>
component let's you track interactions with analytics declaratively.react-storefront/analytics
to make sure they match the updated signatures.react-storefront/model
no longer fire analytics events. Analytics events are only fired front components.proxyUpstream
handlers to render the PWA. Return null or undefined to render the proxied page.app.loading
is true
, instead of being covered by the LoadMask/Skeleton.Renamed Breadcrumbs component to BackNav. It no longer tags an array of breadcrumbs, it now takes a single url and text.
Created a new Breadcrumbs component for displaying multiple breadcrumbs.
UpdateNotification
component that notifies the user when a new version of the app is available.Link
bug which formatted URL's incorrectlySearchDrawer
, which replaces SearchPopup
.proxyUpstream
handler. As a result, support for requestHeaderTransform({ fallbackToAdapt: true })
has been removed. Instead, simple add a fallback(proxyUpstream())
handler to your router.set-cookie
headers from fetch
calls to upstream APIs when not caching on the server.fetchWithCookies
to automatically forward all cookies when calling upstream APIs.handler(params, request, response)
ShowMoreButton
to ShowMore
and added infiniteScroll
propplatform/*
modules.moov-react-dev-server
is no longer neededButtonSelector
component for color and size selectionswindow.history.state
so back and forward transitions are instantaneous.Skeleton
components for creating custom loading skeletons