{"id":3607,"date":"2020-06-15T17:06:59","date_gmt":"2020-06-15T15:06:59","guid":{"rendered":"https:\/\/pagepro.co\/blog\/?p=3607"},"modified":"2026-08-12T14:50:07","modified_gmt":"2026-08-12T12:50:07","slug":"how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days","status":"publish","type":"post","link":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/","title":{"rendered":"How to Build a Headless PWA eCommerce with Saleor, React &#038; GraphQL in 5 Days"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Headless solutions are becoming more and more popular in eCommerce world, and Saleor platform is raising as one of the most interesting in that area. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A few weeks ago a client asked us to build a modern eCommerce system dedicated to the food delivery industry.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As we\u2019re an experienced <a href=\"https:\/\/pagepro.co\/services\/reactjs-development\" target=\"_blank\" rel=\"noreferrer noopener\">React development<\/a> company, focused on the front end to build a full project we had to pick a headless eCommerce solution. We\u2019ve decided to use Saleor because it was a client preference and also matched our technology stack.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As we are an experienced <a href=\"https:\/\/pagepro.co\/services\/pwa-development\" target=\"_blank\" rel=\"noreferrer noopener\">Progressive Web app development company<\/a>, our first step in this project was to lead a discovery stage containing a Proof Of Concept (PoC) Storefront.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"poc-objectives\">PoC Objectives<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Key objectives in this Proof of Concept were to achieve a super fast and offline-working PWA storefront, with support for both rtl &amp; ltr languages and the ability to browse products by categories and add them to cart. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Everything should be fed by Saleor headless eCommerce API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-is-a-storefront\">What is a storefront?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Basically, <strong>a storefront is the part of your shop that everyone sees and it has to attract attention.<\/strong> For online shops, this is the website that users can use to browse the shop and make purchases. Just like a physical storefront, it has to attract attention but also be intuitive and pleasant to use &#8211; this means it has to be blazingly fast even with a poor internet connection or no connection at all! How this can be done?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-is-the-pwa-storefront\">What is the PWA storefront?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here PWA can help us. <strong>PWA means Progressive Web App<\/strong>, in short, this is a website that looks good on either big or small screens but also on mobile devices, it has a \u201cnative feel\u201d &#8211; it looks similar to apps that you install on your device but without the \u201cinstalling\u201d step.&nbsp; Just go to the website and add it to your start screen, that\u2019s it!&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Another big advantage of PWA is <strong>offline capabilities<\/strong>. PWA app constantly caches data, this allows a super-fast working page on subsequent visits, but also the app can work offline! All pages that you visited are cached and you can access them anytime no matter where you are.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-is-headless-ecommerce\">What is headless eCommerce?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Headless eCommerce is nothing else than eCommerce enriched with the headless CMS solution.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thanks to that, <strong>the frontend part is not coupled with the backend <\/strong>anymore, which means you can send the information (products, reviews, stocks, etc.) straight from the backend to any device without any limitation from the frontend side. Later on, the frontend can be presented by the developer in any form or shape you want.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In other words, you don\u2019t have to pre-define the experience for your users (nor admins), and you can use the blessing of (in fact) unlimited experience customization.<\/p>\n\n\n\n<div class=\"wp-block-code-mind-cta c-cta-block\" style=\"background-color:;color:\"><div class=\"c-cta-block__content\"><p class=\"c-cta-block__title\">Is PWA a Good Choice in Your Case? <\/p><div class=\"c-cta-block__action\"><a href=\"https:\/\/pagepro.co\/consultation\" class=\"c-cta-block__button ga-cta ga-cta-consultation theme-bg-3\">SCHEDULE A FREE CALL WITH OUR EXPERT. <\/a><\/div><\/div><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"most-valuable-advantages-of-headless-ecommerce-with-saleor\"><span class=\"underline-accent\">Most valuable advantages of headless eCommerce with Saleor:<\/span><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Faster time to market<\/li>\n\n\n\n<li>Unlimited experience customization<\/li>\n\n\n\n<li>Enables you to go fully omnichannel<\/li>\n\n\n\n<li>Deploy quick changes instantly<\/li>\n\n\n\n<li>Build additional landing pages quickly<\/li>\n\n\n\n<li>Easier for marketing conversion tests<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-is-saleor\">What is Saleor?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Saleor is a complete headless e-commerce platform.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It consists of 3 main parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Saleor Core<\/strong> &#8211; backend written in Python connected with PostgreSQL database.<\/li>\n\n\n\n<li><strong>Dashboard<\/strong> &#8211; Website build using React for store owners members.<\/li>\n\n\n\n<li><strong>Saleor Storefront<\/strong> &#8211; Example website for clients build using React.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"830\" height=\"224\" src=\"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/06\/New-Bitmap-Image-1.bmp\" alt=\"Headless Saleor: Saleor's structure\" class=\"wp-image-3609\"\/><figcaption class=\"wp-element-caption\">Saleor structure<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">It uses GraphQL to communicate with storefront and dashboard.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"building-pwa-storefront-with-saleor-as-a-headless-ecommerce\">Building PWA Storefront with Saleor as a headless eCommerce<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-display-the-categories-listing\"><span class=\"underline-accent\">Step 1: Display the Categories Listing<\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To get data from API we use <a href=\"https:\/\/pagepro.co\/blog\/react-apollo-2022\/\">ApolloClient<\/a> as it is one of the best solutions to communicate using GraphQL.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To get a list of products\/categories we simply execute a GraphQL query.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">More on how the queries should look like on<a href=\"https:\/\/docs.saleor.io\/docs\/developer\/products\" target=\"_blank\" rel=\"noreferrer noopener nofollow\"> https:\/\/docs.saleor.io\/docs\/developer\/products<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example query for getting categories with translations:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">import { gql } from \"apollo-boost\";\n\nexport const categoriesQuery = gql`\n  query categories($language: LanguageCodeEnum!) {\n    shop {\n      name\n      description\n      homepageCollection {\n        backgroundImage {\n          url\n        }\n      }\n    }\n    categories(first: 9) {\n      edges {\n        node {\n          id\n          name\n          translation(languageCode: $language) {\n            name\n          }\n          backgroundImage {\n            url\n          }\n        }\n      }\n    }\n  }\n`;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see using GraphQL we fetch only the data we need which can greatly reduce loading speeds. In this example, we fetch the first 20 categories from the API and we get their id, name, translation (if it exists, else we use the original name) and URL for an image of the category.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-prepare-product-details-page\"><span class=\"underline-accent\">Step 2: Prepare Product Details Page<\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Example query fetching product details. It\u2019s part of Saleor-SDK.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This query is responsible for fetching product details. It utilizes fragments.&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export const productDetails = gql`\n  ${basicProductFragment}\n  ${selectedAttributeFragment}\n  ${productVariantFragment}\n  ${productPricingFragment}\n  query ProductDetails($id: ID!) {\n    product(id: $id) {\n      ...BasicProductFields\n      ...ProductPricingField\n      descriptionJson\n      category {\n        id\n        name\n        products(first: 3) {\n          edges {\n            node {\n              ...BasicProductFields\n              ...ProductPricingField\n              category {\n                id\n                name\n              }\n            }\n          }\n        }\n      }\n      images {\n        id\n        url\n      }\n      attributes {\n        ...SelectedAttributeFields\n      }\n      variants {\n        ...ProductVariantFields\n      }\n      seoDescription\n      seoTitle\n      isAvailable\n    }\n  }\n`;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example product fragment. It\u2019s part of Saleor-SDK.<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export const basicProductFragment = gql`\n  fragment BasicProductFields on Product {\n    id\n    name\n    thumbnail {\n      url\n      alt\n    }\n    thumbnail2x: thumbnail(size: 510) {\n      url\n    }\n  }\n`;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A fragment is a reusable part of the GraphQL query that allows you to put it in different queries without repeating code.<\/p>\n\n\n\n<div class=\"c-newsletter-block-acf\">\n    <p class=\"c-newsletter-block-acf__title c-newsletter__header\">\n        EXPERT INSIGHTS, FRICTIONLESSLY DELIVERED!    <\/p>\n    <p class=\"c-newsletter-block-acf__desc c-newsletter__header\">\n        Curated tech news delivered straight to your inbox every month.\r\n    <\/p>\n    <form method=\"post\" class=\"c-newsletter-block-acf__form js-newsletter-form c-newsletter__action\" name=\"newsletter-block-form\">\n        <input name=\"newsletter-email\" id=\"newsletter-email\" type=\"text\" class=\"c-newsletter-block-acf__input js-newsletter-input\" placeholder=\"Company Email\" \/>\n        <input name=\"newsletter-campaign\" id=\"newsletter-campaign\" type=\"hidden\" value=\"\" \/>\n        <div class=\"c-newsletter-block-acf__group\">\n            <input name=\"consent\" id=\"consent\" type=\"checkbox\" class=\"js-newsletter-consent\" \/>\n            <label class=\"c-newsletter-block-acf__label\" for=\"consent\">I accept the <a href=\"https:\/\/pagepro.co\/privacy-policy\">Privacy Policy<\/a> and agree to process my personal data by Pagepro for marketing purposes.<\/label>\n        <\/div>\n        <input type=\"submit\" class=\"c-newsletter-block-acf__button button js-newsletter-sub ga-newsletter-form-content\" value=\"Sign up\" \/>\n        <p class=\"theme-size-1 js-message-valid is-hidden is-invalid\"><\/p>\n    <\/form>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-add-rtl-arabic-support\"><span class=\"underline-accent\">Step 3: Add RTL Arabic Support<\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The biggest challenge in providing arabic support was right-to-left orientation of the page layout.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A simple solution might be to just add a CSS property <code>direction: rtl<\/code> in the root of the page, but this doesn\u2019t resolve all the issues we might face. For example, if we use images like <code>leftArrowImage<\/code> that expands some menu or we position something absolutely. They won\u2019t react the way we might want just by changing the <code>direction<\/code> CSS property. To resolve this issue I created a store in redux to hold the selected language and its direction. (It may be moved to apolloClient in the future as apollo now can hold local state data). This way any component that need to react for language direction changes can get the current direction of the chosen language.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Action types for setting language in redux:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">export const SET_LANGUAGE = \"SET_LANGUAGE\";\n\ninterface SET_LANGUAGE_ACTION {\n  type: typeof SET_LANGUAGE;\n  payload: {\n    language: Language;\n    direction: Direction;\n  };\n}\n\nexport type LanguageActionsTypes = SET_LANGUAGE_ACTION;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Actions responsible for setting and getting the default language. Also, they change the i18n language for translating local site content:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">import { AnyAction, ActionCreator, Dispatch } from \"redux\";\nimport { ThunkAction } from \"redux-thunk\";\n\nimport Language from \"..\/..\/types\/language\";\nimport { LanguageActionsTypes, SET_LANGUAGE } from \".\/actionTypes\";\nimport Direction from \"..\/..\/types\/direction\";\nimport i18n from \"..\/..\/i18n\";\n\nconst LANGUAGE_KEY = \"language\";\n\nexport const setLanguageAction = (\n  language: Language\n): LanguageActionsTypes => ({\n  type: SET_LANGUAGE,\n  payload: {\n    language,\n    direction: language === Language.ar ? Direction.rtl : Direction.ltr,\n  },\n});\n\nexport const setLanguage: ActionCreator&lt;ThunkAction&lt;\n  void,\n  {},\n  Language,\n  AnyAction\n>> = (language: Language) => (dispatch: Dispatch) => {\n  setLanguageOnLocalStorage(language);\n  dispatch(setLanguageAction(language));\n\n  i18n.changeLanguage(language?.toLowerCase());\n};\n\nexport const getLanguageFromLocalStorage = (): Language => {\n  try {\n    const language = localStorage.getItem(LANGUAGE_KEY) as Language;\n    if (language) return language;\n    else return Language.en;\n  } catch {\n    return Language.en;\n  }\n};\n\nexport const setLanguageOnLocalStorage = (language: Language): void => {\n  try {\n    localStorage.setItem(LANGUAGE_KEY, language);\n  } catch {}\n};\n\nexport const setInitialLanguage: ActionCreator&lt;ThunkAction&lt;\n  void,\n  {},\n  {},\n  AnyAction\n>> = () => (dispatch: Dispatch) => {\n  const language = getLanguageFromLocalStorage();\n  dispatch(setLanguageAction(language));\n\n  i18n.changeLanguage(language?.toLowerCase());\n};<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reducer for the language store:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">import LanguageState from \"..\/..\/types\/languageState\";\nimport { LanguageActionsTypes, SET_LANGUAGE } from \".\/actionTypes\";\n\nconst initialState: LanguageState = {\n  name: null,\n  direction: null,\n};\n\nexport const languageReducer = (\n  state = initialState,\n  action: LanguageActionsTypes\n): LanguageState => {\n  const { type, payload } = action;\n\n  switch (type) {\n    case SET_LANGUAGE:\n      return {\n        ...state,\n        name: payload.language,\n        direction: payload.direction,\n      };\n    default:\n      return state;\n  }\n};\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Selectors allowing easy access to the store:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">import RootState from \"..\/..\/types\/rootState\";\nimport LanguageState from \"..\/..\/types\/languageState\";\nimport Language from \"..\/..\/types\/language\";\nimport Direction from \"..\/..\/types\/direction\";\n\nexport const languagesSelector = (state: RootState): LanguageState =>\n  state?.language;\n\nexport const languageNameSelector = (state: RootState): Language | null =>\n  languagesSelector(state)?.name;\n\nexport const languageDirectionSelector = (state: RootState): Direction | null =>\n  languagesSelector(state)?.direction;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-offline-support\"><span class=\"underline-accent\">Step 4: Offline support<\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To make the app able to work offline I used the <code>serviceWorker<\/code> that is built-in in create-react-app. This makes the site cache its content on <code>localStorage<\/code> of the device thus allows opening all of previously visited pages without the need for an internet connection.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But this way we cache only local content of the page, what about API responses? ApolloClient already has a built-in mechanism to cache responses from our API. But they are session-bound. All we need to do then is save the cache to our local storage using \u201capollo-cache-persist\u201d. This way we can browse the app offline with all of its contents.<br>To make the serviceWorker able to work just look for <code>serviceWorker.unregister()<\/code> line in your <code>index.tsx\/jsx<\/code> file and change it to <code>serviceWorker.register()<\/code>. This will take care of caching all of your site local content on users device when they are browsing your page. Keep in mind that it will not work on development mode. And if you want to test it outside your pc you need an https server (thanks to Tomasz Chmiel).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As I said before ApolloClient already caches all data it fetches from the API but it is session-bound. All we need to do is to save this data to <code>local storage<\/code> so it can be reused even after the user leaves the page. To do this we add <code>apollo-cache-persist<\/code> to our apollo client config.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Apollo client with persist implementation:<\/p>\n\n\n\n<pre class=\"wp-block-code-mind-code c-code\"><code class=\"javascript\">import { ApolloClient } from \"apollo-boost\";\nimport { HttpLink } from \"apollo-link-http\";\nimport { CachePersistor } from \"apollo-cache-persist\";\nimport { InMemoryCache, NormalizedCacheObject } from \"apollo-cache-inmemory\";\nimport { PersistentStorage, PersistedData } from \"apollo-cache-persist\/types\";\n\nimport api from \".\/constants\/api\";\n\nconst SCHEMA_VERSION = \"1\";\nconst SCHEMA_VERSION_KEY = \"apollo-schema-version\";\n\nconst getApolloClient = async () => {\n  const httpLink = new HttpLink({ uri: api.rootApiUrl });\n  const cache = new InMemoryCache();\n\n  const persistor = new CachePersistor({\n    cache,\n    storage: window.localStorage as PersistentStorage&lt;\n      PersistedData&lt;NormalizedCacheObject>\n    >,\n  });\n\n  const currentVersion = window.localStorage.getItem(SCHEMA_VERSION_KEY);\n\n  if (currentVersion === SCHEMA_VERSION) {\n    await persistor.restore();\n  } else {\n    await persistor.purge();\n    window.localStorage.setItem(SCHEMA_VERSION_KEY, SCHEMA_VERSION);\n  }\n\n  return new ApolloClient({ link: httpLink, cache });\n};\n\nexport default getApolloClient;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Source: https:\/\/rubygarage.org\/blog\/pwa-with-react-apollo<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>SCHEMA_VERSION<\/code> allows us to reset the cache on all client devices when we make breaking changes in our local database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusions\">Conclusions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Creating a new Saleor powered storefront from scratch is pretty straightforward and easy. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All basic queries (like fetching products, making purchases) are nicely described in the docs, also GraphQL playground has nice and detailed documentation of every query and mutation, but I had some hard time choosing right product variant by selected product options, probably this will be easier when we will get final SDK for the storefront. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the biggest issue &#8211; <strong>lack of polished and ready to use SDK<\/strong> (when I started working with Saleor there wasn\u2019t any package that you could just npm install to your project). But recently Saleor dev team created a separate ready to use saleor-sdk that you can install. It&#8217;s still under development but it should make development much simpler and more user friendly. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Overall &#8211; <strong>Saleor is pretty straightforward to use and extend<\/strong> but still needs polishing in the case of SDK.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Headless solutions are becoming more and more popular in eCommerce world, and Saleor platform is raising as one of the most interesting in that area. Let us show you how to build a headless PWA ecommerce with Saleor in 5 days.<\/p>\n","protected":false},"author":25,"featured_media":6627,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"rop_custom_images_group":[],"rop_custom_messages_group":[],"rop_publish_now":"initial","rop_publish_now_accounts":[],"rop_publish_now_history":[],"rop_publish_now_status":"pending","footnotes":""},"categories":[318],"tags":[],"class_list":["post-3607","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How to Build a Headless PWA eCommerce with Saleor, React &amp; GraphQL in 5 Days - Pagepro<\/title>\n<meta name=\"description\" content=\"If you want to have a headless and a super-fast PWA ecommerce storefront in React, GraphQL &amp; Saleor in 5 days, I will guide you through step-by-step.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build a Headless PWA eCommerce with Saleor, React &amp; GraphQL in 5 Days - Pagepro\" \/>\n<meta property=\"og:description\" content=\"If you want to have a headless and a super-fast PWA ecommerce storefront in React, GraphQL &amp; Saleor in 5 days, I will guide you through step-by-step.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/\" \/>\n<meta property=\"og:site_name\" content=\"Pagepro\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/thisispagepro\" \/>\n<meta property=\"article:published_time\" content=\"2020-06-15T15:06:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-12T12:50:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2021\/03\/saleor.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"682\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Daniel Ni\u017cy\u0144ski\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Daniel Ni\u017cy\u0144ski\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/\"},\"author\":{\"name\":\"Daniel Ni\u017cy\u0144ski\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#\\\/schema\\\/person\\\/adda50101116bf3d52a8e51025bcdae5\"},\"headline\":\"How to Build a Headless PWA eCommerce with Saleor, React &#038; GraphQL in 5 Days\",\"datePublished\":\"2020-06-15T15:06:59+00:00\",\"dateModified\":\"2026-08-12T12:50:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/\"},\"wordCount\":1359,\"publisher\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/saleor.png\",\"articleSection\":[\"Web Dev\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/\",\"url\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/\",\"name\":\"How to Build a Headless PWA eCommerce with Saleor, React & GraphQL in 5 Days - Pagepro\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/saleor.png\",\"datePublished\":\"2020-06-15T15:06:59+00:00\",\"dateModified\":\"2026-08-12T12:50:07+00:00\",\"description\":\"If you want to have a headless and a super-fast PWA ecommerce storefront in React, GraphQL & Saleor in 5 days, I will guide you through step-by-step.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#primaryimage\",\"url\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/saleor.png\",\"contentUrl\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/saleor.png\",\"width\":1200,\"height\":682,\"caption\":\"A graphic with a glowing red atomic model on the left and a bold red background on the right, featuring a stylised blue S and two overlapping rectangles in blue and red, visually answering what is Saleor ecommerce.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/pagepro.co\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog\",\"item\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"How to Build a Headless PWA eCommerce with Saleor, React &#038; GraphQL in 5 Days\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/\",\"name\":\"Pagepro\",\"description\":\"Frictionless Next.js, Expo &amp; Sanity Development Blog\",\"publisher\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#organization\",\"name\":\"Pagepro\",\"url\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/08\\\/logo_pagepro-b66d228a1e-1.png\",\"contentUrl\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/08\\\/logo_pagepro-b66d228a1e-1.png\",\"width\":440,\"height\":200,\"caption\":\"Pagepro\"},\"image\":{\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/thisispagepro\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/#\\\/schema\\\/person\\\/adda50101116bf3d52a8e51025bcdae5\",\"name\":\"Daniel Ni\u017cy\u0144ski\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f72093a6db1e7ee320e3a06733bec105cf5769bde90ff976d4e17289c6e99fa0?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f72093a6db1e7ee320e3a06733bec105cf5769bde90ff976d4e17289c6e99fa0?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f72093a6db1e7ee320e3a06733bec105cf5769bde90ff976d4e17289c6e99fa0?s=96&d=mm&r=g\",\"caption\":\"Daniel Ni\u017cy\u0144ski\"},\"description\":\"Daniel is a distinguished Senior React Native Developer at Pagepro, renowned for his exceptional problem-solving skills and proactive approach. His journey in mobile app development is marked by a series of successfully tackled challenges, where he has consistently resolved some of the most intricate issues in mobile app development.\",\"sameAs\":[\"https:\\\/\\\/www.linkedin.com\\\/in\\\/daniel-ni\u017cy\u0144ski-17322a1b3\\\/\"],\"url\":\"https:\\\/\\\/pagepro.co\\\/blog\\\/author\\\/daniel_nizynski\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How to Build a Headless PWA eCommerce with Saleor, React & GraphQL in 5 Days - Pagepro","description":"If you want to have a headless and a super-fast PWA ecommerce storefront in React, GraphQL & Saleor in 5 days, I will guide you through step-by-step.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/","og_locale":"en_US","og_type":"article","og_title":"How to Build a Headless PWA eCommerce with Saleor, React & GraphQL in 5 Days - Pagepro","og_description":"If you want to have a headless and a super-fast PWA ecommerce storefront in React, GraphQL & Saleor in 5 days, I will guide you through step-by-step.","og_url":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/","og_site_name":"Pagepro","article_publisher":"https:\/\/www.facebook.com\/thisispagepro","article_published_time":"2020-06-15T15:06:59+00:00","article_modified_time":"2026-08-12T12:50:07+00:00","og_image":[{"width":1200,"height":682,"url":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2021\/03\/saleor.png","type":"image\/png"}],"author":"Daniel Ni\u017cy\u0144ski","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Daniel Ni\u017cy\u0144ski","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#article","isPartOf":{"@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/"},"author":{"name":"Daniel Ni\u017cy\u0144ski","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/person\/adda50101116bf3d52a8e51025bcdae5"},"headline":"How to Build a Headless PWA eCommerce with Saleor, React &#038; GraphQL in 5 Days","datePublished":"2020-06-15T15:06:59+00:00","dateModified":"2026-08-12T12:50:07+00:00","mainEntityOfPage":{"@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/"},"wordCount":1359,"publisher":{"@id":"https:\/\/pagepro.co\/blog\/#organization"},"image":{"@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#primaryimage"},"thumbnailUrl":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2021\/03\/saleor.png","articleSection":["Web Dev"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/","url":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/","name":"How to Build a Headless PWA eCommerce with Saleor, React & GraphQL in 5 Days - Pagepro","isPartOf":{"@id":"https:\/\/pagepro.co\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#primaryimage"},"image":{"@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#primaryimage"},"thumbnailUrl":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2021\/03\/saleor.png","datePublished":"2020-06-15T15:06:59+00:00","dateModified":"2026-08-12T12:50:07+00:00","description":"If you want to have a headless and a super-fast PWA ecommerce storefront in React, GraphQL & Saleor in 5 days, I will guide you through step-by-step.","breadcrumb":{"@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#primaryimage","url":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2021\/03\/saleor.png","contentUrl":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2021\/03\/saleor.png","width":1200,"height":682,"caption":"A graphic with a glowing red atomic model on the left and a bold red background on the right, featuring a stylised blue S and two overlapping rectangles in blue and red, visually answering what is Saleor ecommerce."},{"@type":"BreadcrumbList","@id":"https:\/\/pagepro.co\/blog\/how-to-build-a-headless-pwa-ecommerce-with-saleor-react-graphql-in-5-days\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/pagepro.co\/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https:\/\/pagepro.co\/blog\/"},{"@type":"ListItem","position":3,"name":"How to Build a Headless PWA eCommerce with Saleor, React &#038; GraphQL in 5 Days"}]},{"@type":"WebSite","@id":"https:\/\/pagepro.co\/blog\/#website","url":"https:\/\/pagepro.co\/blog\/","name":"Pagepro","description":"Frictionless Next.js, Expo &amp; Sanity Development Blog","publisher":{"@id":"https:\/\/pagepro.co\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/pagepro.co\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/pagepro.co\/blog\/#organization","name":"Pagepro","url":"https:\/\/pagepro.co\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/08\/logo_pagepro-b66d228a1e-1.png","contentUrl":"https:\/\/pagepro.co\/blog\/wp-content\/uploads\/2020\/08\/logo_pagepro-b66d228a1e-1.png","width":440,"height":200,"caption":"Pagepro"},"image":{"@id":"https:\/\/pagepro.co\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/thisispagepro"]},{"@type":"Person","@id":"https:\/\/pagepro.co\/blog\/#\/schema\/person\/adda50101116bf3d52a8e51025bcdae5","name":"Daniel Ni\u017cy\u0144ski","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/f72093a6db1e7ee320e3a06733bec105cf5769bde90ff976d4e17289c6e99fa0?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f72093a6db1e7ee320e3a06733bec105cf5769bde90ff976d4e17289c6e99fa0?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f72093a6db1e7ee320e3a06733bec105cf5769bde90ff976d4e17289c6e99fa0?s=96&d=mm&r=g","caption":"Daniel Ni\u017cy\u0144ski"},"description":"Daniel is a distinguished Senior React Native Developer at Pagepro, renowned for his exceptional problem-solving skills and proactive approach. His journey in mobile app development is marked by a series of successfully tackled challenges, where he has consistently resolved some of the most intricate issues in mobile app development.","sameAs":["https:\/\/www.linkedin.com\/in\/daniel-ni\u017cy\u0144ski-17322a1b3\/"],"url":"https:\/\/pagepro.co\/blog\/author\/daniel_nizynski\/"}]}},"_links":{"self":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts\/3607","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/comments?post=3607"}],"version-history":[{"count":34,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts\/3607\/revisions"}],"predecessor-version":[{"id":23560,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/posts\/3607\/revisions\/23560"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/media\/6627"}],"wp:attachment":[{"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/media?parent=3607"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/categories?post=3607"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pagepro.co\/blog\/wp-json\/wp\/v2\/tags?post=3607"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}