snow-mountain

Build an E-Commerce App With Shopify and Next.js


Last updated on
Next.jsShopifyWeb Development

Summary (TL;DR): In this article, we will learn how to build an e-commerce application with Shopify and Next.js. We’ll look at how to create a Shopify store, and how to use our Storefront API. We’ll also learn how to display all our products and connect our application before deploying it. 

Over the years, there’s been a high rise in e-commerce needs. Most business owners have turned to Shopify and WooCommerce to set up online stores. 

Although Shopify allows users to create online stores, developers can extend their stores by creating frontend applications and connecting them using Shopify APIs. Not only that, but developers stand to benefit from this as these apps can further be monetized.  

This article will teach you how to create a Shopify store, set one up with goods, and get our Storefront API access key. We will then create a Next.js application and connect our Shopify store to it to fetch products stored on the Shopify store.

While You Are at It, Learn About Mastering State Management in Next.js

Table of Contents

Creating a Shopify Store
Get Your Shopify Storefront API Access Token.
Create Next.js Application
Creating Components and E-Commerce App Pages
Creating a Product Details Page
Connecting the Next.js App With Shopify

Creating a Shopify Store

To create a Shopify store, navigate the Shopify webpage and sign up for a free trial. Next, navigate to your store and design collections. Collections are usually product categories; we will create three collections. You can make more. 

To create a collection, on the side navigation, click on Products and then click on Collections, as seen in the image below:

Next, click the Create Collection button and add a title to your collection; in our case, we will create a Men and Women collection. Next, click on Manual as the collection type and manually add all the information about your Store, as shown below:

After creating our Collections, we will develop Products and add a product to our collections. To do that, click on the product tab and the button Add product, as seen below: 

To create a product, take the steps below: 

  • Enter the title for the product 
  • add a picture of the product
  • Add a type of product, and an example would be dresses or shoes 
  • Add a collection for the product 
  • Add a price for the product 
  • In the inventory section of your dashboard, add the quantity and availability of the product. 
  • Lastly, set your product status as active
  • Save your product!

Afterward, this will be an active product in your Shopify store. You can repeat these steps and create as many products as you want.

Get Your Shopify Storefront API Access Token

To get your API access token, navigate to your Apps tab on your left-hand navigation, and click on Apps and Sales Channel settings as shown below:

Next, click on the Develop apps for your store button, as shown in the image below:

Next, click on the Create an app button and add your app details like name and link:

After creating a new app, navigate to the Configuration tab and click on the Configure Storefront API scope tab, as shown in the image below:

Once you’ve done this, click on the checkboxes on Checkout, Content, and Customers. Next, go to your API credentials tab and save it.

Create Next.js Application

In your terminal, type the command below to create a Next.js application in your designated directory to create a Next.js application: 

npx create-next-app shopify-app

We will be using MUI for styling purposes as the library allows for quick UI building. You can install it using the command below:

npm install --save @emotion/react @emotion/styled @mui/icons-material @mui/material

We will create a list of dummy data to get the application up and running quickly. Create a data.js file to hold the dummy products in the root folder:

const Graate_PRODUCTS = [
    {
      handle: 1,
      name: 'Yeezy Slides',
      price: '$500',
      collection: 'men',
      image: 'https://images.unsplash.com/photo-1626771652942-93d834f3d1fd?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8M3x8eWVlenklMjBzbGlkZXN8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60'
    },
    {
      handle: 2,
      name: 'Women top',
      price: '$400',
      collection: 'women',
      image: 'https://images.unsplash.com/photo-1416339698674-4f118dd3388b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NDR8fHdvbWVuJTIwdG9wc3xlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60'
    },
    {
      handle: 3,
      name: 'Biker jackets',
      price: '$600',
      collection: 'men',
      image: 'https://images.unsplash.com/photo-1591047139829-d91aecb6caea?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8amFja2V0c3xlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60'
    },
    {
        handle: 4,
        name: 'Ashawo shorts',
        price: '$200',
        collection: 'men',
        image: 'https://images.unsplash.com/photo-1532788031780-73fddc7e94fe?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8OXx8c2hvcnR8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60'
      },
      {
        handle: 5,
        name: 'Dinner gowns',
        price: '$350',
        collection: 'women',
        image: 'https://images.unsplash.com/photo-1630361084015-9a5bf5f4950e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8ZGlubmVyJTIwZ293bnN8ZW58MHx8MHx8&auto=format&fit=crop&w=400&q=60'
      },
  ];
  export default Graate_PRODUCTS;

Creating Components and E-Commerce App Pages

In the root folder, create a component folder that will hold the various components for our application. 

Inside the component folder, create a component called ProductList.js. This component will be in charge of listing the products of our store.  Inside this component, we will be using some prebuilt MUI components to create a list with images in a grid format:

import * as React from 'react';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
export default function ProductsList({ products }) {
  return (
    (products && products.length > 0) ?
    <ImageList cols={5} gap={20}>
      {products.map((product) => (
        <ImageListItem key={product.image}>
          <img
            src={`${product.image}?w=250&fit=crop&auto=format`}
            srcSet={`${product.image}?w=250&fit=crop&auto=format&dpr=2 2x`}
            alt={product.name}
            loading="lazy"
          />
          <ImageListItemBar
            title={product.name}
            subtitle={<span>Price: {product.price}</span>}
            position="below"
          />
        </ImageListItem>
      ))}
    </ImageList>:
    <Typography variant="body1" align="center">There are no products in this collection</Typography>
  );
}

We will need a navigation bar set up with routing for our application. We will use the MUI prebuilt AppBar and Toolbar components to create this. Create a Navigation.js component in the component folder and paste in the code below:

import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';


    export default function Navigation() {
      return (
        <AppBar position="static">
          <Toolbar>
            <Typography mr={2}>All products</Typography>
            <Typography mr={2}>Women</Typography>
            <Typography>Men</Typography>
          </Toolbar>
        </AppBar>
      )
    };

Now import the built components into your index.js file:

import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Navigation from '../components/Navigation';
import ProductsList from '../components/ProductsList';
import Graate_PRODUCTS from '../data.js';
export default function Index() {
  return (
    <Box>
      <Navigation />
      <Container maxWidth="lg">
        <ProductsList products={Graate_PRODUCTS} />
      </Container>
    </Box>
  );
}

Run npm run dev to start up the development server and see the results on the browser.

With the homepage all done, we will need pages to handle some specific collections of catalogs as seen in most online stores nowadays. We will use Next.js inbuilt dynamic routing system to create routes to those particular pages.

Create a collections folder inside the page’s directory. Inside the collections folder, create a [collectionName].js file. Next, we will use the useRouter hook from Next.js to get the params from the URL, filter them and pass the filtered list to the ProductsList:

import * as React from 'react';
import { useRouter } from 'next/router';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Navigation from '../../components/Navigation';
import BreadcrumbsNavigation from '../../components/BreadcrumbsNavigation';
import ProductsList from '../../components/ProductsList';
import PRODUCTS from '../../data.js';
export default function CollectionPage() {
  const router = useRouter();
  const { collectionName } = router.query;
  const products = PRODUCTS.filter(
    (product) => product.collection === collectionName
  );
  return (
    <Box>
      <Navigation />
      <Container maxWidth="lg">
        <BreadcrumbsNavigation collection={collectionName} />
        <ProductsList products={products} />
      </Container>
    </Box>
  );
}

We will also create some breadcrumbs for our store to show where the user is at any point. Create a BreadcrumbsNavigation.js file and use the MUI prebuilt Breadcrumbs component. We will also add the Link component for routing:

import * as React from 'react';
import Box from '@mui/material/Box';
import Breadcrumbs from '@mui/material/Breadcrumbs';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';
export default function BreadcrumbsNavigation({ title }) {
  return (
    <Box mt={2}>
      <Breadcrumbs separator=">" aria-label="breadcrumb">
        <Link underline="hover" key="1" color="inherit" href="/">
          Products
        </Link>
        <Typography key="3" color="text.primary">
          {title && title.replace(/^\w/, (c) => c.toUpperCase())}
        </Typography>
      </Breadcrumbs>
    </Box>
  );
}

In the Navigation.js file, import the Link component and add some routing to the app bar:

import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';

export default function Navigation() {
  return (
    <AppBar position="static">
      <Toolbar>
        <Link href="/" underline="none" color="inherit">
          <Typography mr={2}>All Collections</Typography>
        </Link>
        <Link href="/collections/women" underline="none" color="inherit">
          <Typography mr={2}>Women</Typography>
        </Link>
        <Link href="/collections/men" underline="none" color="inherit">
          <Typography mr={2}>Men</Typography>
        </Link>
        <Link href="/collections/cats" underline="none" color="inherit">
          <Typography mr={2}>Cats</Typography>
        </Link>
        <Link href="/collections/dogs" underline="none" color="inherit">
          <Typography mr={2}>Dogs</Typography>
        </Link>
      </Toolbar>
    </AppBar>
  );
}

To see our progress so far, as shown in the image below, head to http://localhost:3000/ and test out the different routes on the Tab bar.

Creating a Product Details Page

With our Homepage all set up, let’s create a product details page that will dynamically display each product’s details. 

Create a products folder, and add a [productDetails].js file. Next, we get the param from the URL and then use it for getting the product. We could use the Next image component with image optimization to display our product images. To do that, add the image domain name to your next.config.js file and remember to restart your server after:

module.exports = {
  reactStrictMode: true,
  images: {
    domains: ['images.unsplash.com'],
  },
};

Next, add some routing to the product page by adding an onClick event which will take us to productPage:

import * as React from 'react';
    import { useRouter } from 'next/router'


    ...


    export default function ProductsList({products}) {
      const router = useRouter()
      // takes us to product's page
      const goToProductPage = productHandle => router.push(`/products/${productHandle}`)


      return (
        <Box>
          {
            (products && products.length > 0) ?
            <ImageList cols={5} gap={20}>
              {products.map((product) => (
                <ImageListItem
                  key={product.image}
                  style={{cursor: 'pointer'}}
                  onClick={() => goToProductPage(product.handle)}>
                  ...
                </ImageListItem>
              ))}
            </ImageList>:
            <Typography variant="body1" align="center">There are no products in this collection</Typography>
          }
        </Box>
      )
    };

Navigate to the browser and see the results:

Connecting Next.js App With Shopify

We are done creating the application’s client-side, but it relies on dummy data. So in this section, we will look at fetching data from our Shopify store and rendering them.

For this, we will be making use of the Shopify Buy Javascript SDK, which you can install with the command below:

npm install Shopify-buy
Next, we will get our Storefront API tokens and our Shopify domain and store them in a created .env.local file in the root project:

SHOPIFY_STORE_FRONT_ACCESS_TOKEN=*****
  SHOPIFY_STORE_DOMAIN=********

You can find your domain by clicking on the Settings tab:

Next, create a folder in the root project called lib and create a shopify.js file inside it. This folder will hold the Shopify-buy library and the Shopify variables.We will create a parseShopifyResponse function which will hold the stringified and parsed response from our API call to Shopify and can be used repeatedly throughout our application:

import Client from 'shopify-buy';
export const shopifyClient = Client.buildClient({
  storefrontAccessToken: process.env.SHOPIFY_STORE_FRONT_ACCESS_TOKEN,
  domain: process.env.SHOPIFY_STORE_DOMAIN,
});
export const parseShopifyResponse = (response) =>
  JSON.parse(JSON.stringify(response));

In the index.js file, we will use the getServerSideProps to make an API call when any page is requested. We will also use the shopifyClient.product.fetchAll() to get all the products in the store. The returned data will be passed to the ProductsList components as props.

Now Shopify’s response format is not the same as the dummy data format. Meaning that we have to adapt the  ProductsList.js to Shopify’s data. We will create a Product component that will grab your product’s ID, image, handle, title, and price.

Shopify’s product comes with a handle field generated when new products are created, which will be helpful when navigating to the products page.
Below is the updated ProductList.js file:

import * as React from 'react';
import { useRouter } from 'next/router';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
const Product = ({ product, goToProductPage }) => {
  const { id, title, images, variants, handle } = product;
  const { src: productImage } = images[0];
  const { price } = variants[0];
  return (
    <ImageListItem
      style={{ cursor: 'pointer' }}
      onClick={() => goToProductPage(handle)}
    >
      <img
        src={`${productImage}?w=250&auto=format`}
        srcSet={`${productImage}?w=250&auto=format&dpr=2 2x`}
        alt={title}
        loading="lazy"
      />
      <ImageListItemBar
        title={title}
        subtitle={<span>Price: {price}</span>}
        position="below"
      />
    </ImageListItem>
  );
};
export default function ProductsList({ products }) {
  const router = useRouter();


  // Navigate to product page with handle i.e /products/black-converses
  const goToProductPage = (productHandle) =>
    router.push(`/products/${productHandle}`);
  return (
    <Box>
      {products && products.length > 0 ? (
        <ImageList cols={5} gap={20}>
          {products.map((product) => (
            <Product
              key={product.handle}
              product={product}
              goToProductPage={goToProductPage}
            />
          ))}
        </ImageList>
      ) : (
        <Typography variant="body1" align="center">
          There are no products in this collection
        </Typography>
      )}
    </Box>
  );
}

Shopify’s SDK has a function for fetching a single product from the handle called the fetchByHandle function. In the products/[productHandle].js, use the productHandle to get the product and add it to the props. Next, update the product page by grabbing the handle for the query.

As you did with your list of products, you must now update your product page. The following can be used on your page to grab your title, image, and price:

import * as React from 'react';
import Image from 'next/image';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Navigation from '../../components/Navigation';
import BreadcrumbsNavigation from '../../components/BreadcrumbsNavigation';
import ProductsList from '../../components/ProductsList';
import { shopifyClient, parseShopifyResponse } from '../../lib/shopify';
export default function ProductPage({ product }) {
  const { id, title, images, variants, handle } = product;
  const { src: productImage } = images[0];
  const { price } = variants[0];
  return (
    <Box>
      <Navigation />
      {product && (
        <Container maxWidth="lg">
          <BreadcrumbsNavigation title={title} />
          <Grid container direction="row">
            <Grid item xs={6}>
              <Image
                src={productImage}
                alt={`Picture of ${title}`}
                width={500} //  automatically provided
                height={500}//Automatically provided
              />
            </Grid>
            <Grid item xs={6}>
              <Typography variant="h3" my={2}>
                {title}
              </Typography>
              <Grid mt={4}>
                <Typography variant="h6" component="span">
                  Price:{' '}
                </Typography>
                <Typography variant="body1" component="span">
                  {price}
                </Typography>
              </Grid>
              <Grid mt={1}>
                <Button variant="contained">Add to cart</Button>
              </Grid>
            </Grid>
          </Grid>
        </Container>
      )}
    </Box>
  );
}
export const getServerSideProps = async ({ params }) => {
  const { productHandle } = params;
  // Fetch one product
  const product = await shopifyClient.product.fetchByHandle(productHandle);
  return {
    props: {
      product: parseShopifyResponse(product),
    },
  };
};

The collection page will need some filtering, and we can use the client.collection.fetchAllWithProducts() to do that because the Shopify SDK only comes with the option to fetch products by the collection’s ID. Once you get them, then pass them to the props:

export const getServerSideProps = async ({ params }) => {
  const { collectionName } = params;
  // Fetch all the collections
  const collectionsData = await shopifyClient.collection.fetchAllWithProducts();
  const collections = parseShopifyResponse(collectionsData);
  // Get the right one
  const collection = collections.find(
    (collection) => collection.handle === collectionName
  );
  return {
    props: {
      collectionName,
      products: collection.products,
    },
  };
};

Update the next.config.js file to add Shopify’s image domain:

module.exports = {
  reactStrictMode: true,
  images: {
    domains: ['images.unsplash.com', 'cdn.shopify.com'],
  },
};

The final result should look like the image below:

You can find the complete code here.

Conclusion

We have looked at how to set up a Shopify store, enable your Storefront API and get your access token in this article. We also looked at how to create a Next.js frontend for our store and how to set up the Shopify JS SDK.