Next.js E-Commerce Tutorial: Quick Shopping Cart Integration

In a rush? Skip to technical tutorial or live demo

Each time we come back to React-related topics on the blog, its ecosystem seems to have gotten larger, more mature, and efficient.

These days, there isn’t much you can’t do with React, whether you’re a seasoned developer or a complete beginner.

This is mostly due to the creation of tools such as Next.js that have successfully simplified React frontend development.

So, today, I want to explore what Next.js can do for e-commerce.

In the technical tutorial below, I’ll show you how to:

  • Set up a Next.js development environment
  • Create new pages & components
  • Fetch data & import components
  • Add a shopping cart to a Next.js app
  • Style & deploy the app

But before we go through this, let’s make sure we understand what Next.js is and how it can improve your next e-commerce projects.

What is Next.js?

In a nutshell, Next.js is a lightweight framework for React applications.

next-js

Right out of the gate, I feel this definition has the potential to confuse some. Isn’t React already a framework for JavaScript in itself? Where does it end, right?

Well, Next takes all the good parts of React and makes it even easier to get an app running. It does this thanks to multiple built-in configurations—automatic code splitting, file-system routing, server-side rendering, static files exporting, and styling solutions.

Trust me when I say that you can build a LOT of different things with Next.js:

  • Static websites—we’ve listed it as one of the top 5 SSGs for 2019.
  • Progressive Web Apps (PWAs)
  • Server-rendered applications
  • SEO-friendly websites—as we’ve demonstrated here.
  • Mobile apps

It was built by Zeit back in 2016 and has quickly gained traction to the point of becoming one of the most popular tools of its kind. I mean, it’s used by Marvel, Netflix, Uber, Nike… and the list goes on.

Okay, this is all great, and I’m genuinely excited to play with Next here. But is it any good for e-commerce?

Next.js & e-commerce: a good fit?

next-js-for-ecommerce

Like any static site generator or JS framework out there, one of its most significant advantages (vs more traditional e-commerce platforms) is the freedom it gives to developers to create a kickass shopping UX.

The power of the JAMstack right here!

We’ve covered the general React e-commerce ecosystem and its benefits in an earlier post. I would strongly suggest reading it to further understand why it’s a great fit.

But on the probable chance that you're pressed for time, here’s a TL;DR:

→ The use of components for flexibility.

Component-based development enables easy code reuse through your app, but also the writing of small features. Or, in our case, small e-commerce functionalities. This comes in handy once you start scaling and expanding your shopping cart integration.

→ Virtual DOM for performance.

React’s virtual DOM provides a more efficient way of updating the view in a web application. Performance is HUGE in e-commerce; every milli-seconds count.

Speed = Better UX & SEO = $$$.

→ Popularity & vast community for peace of mind.

Any issue has probably been documented already, so you're likely to find a solution to any potential pitfalls in your way.

Next.js features like server-side rendering and static exporting push these React benefits even further by guaranteeing that your website/app will be SEO-friendly. This is something vital to any e-commerce business.

Still need social proof? Here are some Next e-commerce site examples.

Technical tutorial: a Next.js e-commerce SPA

next-js-ecommerce

Okay, time to jump into code and create our own handcrafted Next.js e-commerce app, with the help of Snipcart. Bear with me; this is going to be fun!

Pre-requisites

  • Basic understanding of single-page applications (SPAs)
  • A Snipcart account (forever free in Test mode)

Basic knowledge of React & TypeScript might also help you here, but not mandatory to follow along.

1. Setting up the development environment

Before getting started, you'll need to create a directory for your project and initialize it as a npm repository with the following command:

npm init -y

Once this is done, you'll need to install the dependencies for your project. In this tutorial, I'll use TypeScript and Sass.

Therefore, on top of the regular Next.js dependencies, you'll need to install all the typings as well as @zeit/next-typescript, @zeit/next-sass and node-sass.

To do so, simply run this npm command:

npm install --save react @types/react react-dom @types/react-dom next @types/next @zeit/next-typescript @zeit/next-sass node-sass

You’ll also need to make a few configuration changes. Create a file at the root of your project named next.config.js with the following code:

const withTypescript = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')
module.exports = withTypescript(withSass());

This will indicate to Next.js that you want to use TypeScript and Sass in the project. You'll also need to create a file named .babelrc.js in the same place with the following code:

module.exports = {
  presets: ['next/babel', '@zeit/next-typescript/babel']
}

Furthermore, in your package.json file add the following scripts:

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

By adding these scripts, you'll be able to serve your application locally with the npm run dev command. Don't panic if it doesn't work at this stage, as your application is not ready yet.

2. Creating a new page

Now that your environment is ready to go, you can start adding pages to the site. Inside a pages directory, create a index.tsx file with the following code:

const Index = () => {
  return (
    <div className="app">
      <p>Hello world!</p>
    </div>
  )
}
export default Index

If you're familiar with React, you'll notice that I'll use React Hooks here, which is essentially a functional approach at writing React code. Keep in mind that this feature is entirely opt-in though, so feel free to convert the functions into a class based component if it makes you feel more at home.

At this stage, running npm run dev in your console should serve your application at the following URL: localhost:3000.

3. Generating new components

Since you're building an e-commerce app, you'll need to create four main components inside a components directory: Header.tsx, Footer.tsx, ProductList.tsx and Product.tsx.

Inside the header, you can import the next/link package which allows you to convert most HTML elements into links. In this case, the logo and title will let us go back to the homepage.

Keep in mind that a Link component can only have one nested HTML element and should only be used to send the user to your website. Anything that links outside your website should remain inside an a tag.

Also, using Next.js, you can serve any static content such as images by placing them inside a static directory at the root of your folder.

import Link from "next/link";
export default function Header() {
  return (
    <header className="header">
      <Link href="/">
        <img src="/static/logo.svg" alt="" className="header__logo" />
      </Link>
      <Link href="/">
        <h1 className="header__title">FishCastle</h1>
      </Link>
      <a className="header__summary snipcart-checkout snipcart-summary" href="#" style={{textDecoration: "none"}}>
        <span className="header__price snipcart-total-price"></span>
      </a>
    </header>
  )
}
export default function Footer(){
  return (
    <footer className="footer">
      <p>
        Next.js app with a <a href="https://snipcart.com">Snipcart</a> - powered store
      </p>
    </footer>
  )
}

The Product component will output whatever information you want to display about a particular product. You can create an IProduct interface that matches with Snipcart's product definition and an IProductProps interface to define the types of our props which is passed as a parameter to the function.

import {withRouter, RouterProps} from 'next/router'
export interface IProduct {
  id: string
  name: string
  price: number
  url: string  
  description: string
  image: string
}
interface IProductProps {
  product: IProduct
  router: RouterProps
}
const Product = (props: IProductProps) => {
  return (
    <div className="product">
      <h2 className="product__title">{props.product.name}</h2>
      <p className="product__description">{props.product.description}</p>
      <img src={props.product.image} alt="" className="product__image"/>
      <div className="product__price-button-container">
        <div className="product__price">${props.product.price.toFixed(2)}</div>
        <button 
          className="snipcart-add-item product__button"
          data-item-id={props.product.id}
          data-item-name={props.product.name}
          data-item-price={props.product.price}
          data-item-url={props.router.pathname}
          data-item-image={props.product.image}>
          Add to cart
        </button>
      </div>
    </div>
  )
}
export default withRouter(Product)

Notice how in this component, I've made use of next/router by exporting the component inside the withRouter function?

This is because the router allows you to get the URL both on the client and the server, which is great on many levels—1) the Next.js app can be rendered server-side and 2) Snipcart will be able to crawl back the page to validate the integrity of the product without any issues.

The ProductList.tsx component is going to be used to display a list of products on the homepage. Therefore, you can create a IProductListProps interface that describes an array of IProduct which is eventually going to be passed by our website.

import Product, { IProduct } from "./Product"
interface IProductListProps {
  products: IProduct[]
}
const ProductList = (props: IProductListProps) => {
  return (
    <div className="product-list">
      {props.products.map((product, index) => <Product product={product} key={index}/>)}
    </div>
  )
}
export default ProductList

Remember that with React you need to have a unique key for each child components you decide to render more than once. That is why you need to pass the index as a prop to our Product component.

4. Fetching data and importing components

At this stage, you'll probably want to populate your products to the ProductList component. You could use React's useEffect lifecycle inside the ProductList to fill the data. However, this won't get rendered on the server.

Thankfully Next.js adds a new lifecycle method for pages named getInitalProps, which is an async method that can return anything resolvable into a JavaScript Object. This is where you will generally want to fetch from an API or a CMS.

In this case, you'll simply return a new JavaScript object containing a list of all the products.

You can import your newly created components inside the index.tsx page and add the getIntialProps method by changing your code to the following:

import Header from "../components/Header"
import ProductList from "../components/ProductList"
import { IProduct } from "../components/Product"
import Footer from "../components/Footer"
import Head from "next/head"
interface IIndexProps {
  products: IProduct[]
}
const Index = (props: IIndexProps) => {
  return (
    <div className="app">
      <Header />
      <main className="main">
        <ProductList products={props.products} />
      </main>
      <Footer />
    </div>
  )
}
Index.getInitialProps = async ({ req }) => {
  return {
    products: [
      {id: "nextjs_halfmoon", name: "Halfmoon Betta", price: 25.00, image: "../static/halfmoon.jpg", description: "The Halfmoon betta is arguably one of the prettiest betta species. It is recognized by its large tail that can flare up to 180 degrees."} as IProduct,
      {...}
    ]
  }
}
export default Index

5. Importing Snipcart

Now, let's install Snipcart into the website. First, you'll need to import the Head component from next/head inside your index.tsx page which will allow you to add HTML inside the <head> element.

You can do so by adding the following code inside the Index function return clause:

<Head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
  <script src="https://cdn.snipcart.com/scripts/2.0/snipcart.js" data-api-key="[YOUR-API-KEY]" id="snipcart"></script>
  <link href="https://cdn.snipcart.com/themes/2.0/base/snipcart.min.css" rel="stylesheet" type="text/css" />
</Head>

Don't forget to swap the data-api-key attribute with your own API key ;)

6. Styling your app

So far, you've already set up all the configurations necessary to use Sass inside your web app. Therefore, the only thing left to do is to create a .sscs file and import it inside the page of your liking.

import "../styles.scss"

That said, Next.js offers many other ways to style your web app. For instance, you could add inline styles or use styled-jsx, which is bundled by default inside Next.js applications.

7. Deploying your app

With Next.js, there are two main ways of deploying your application. You can either use a more traditional server-side rendered approach, which is great for web apps with a lot of dynamic content, or export every page to a .html file and serve those files through a content delivery network.

Since we've already explored the latter in this React SEO tutorial, we'll make our app server-side rendered running on a Heroku server.

First, make sure you already have a Heroku account and installed Heroku's CLI.

Once this is done, modify the start script in the package.json file to the following:

next start -p $PORT

Now create your Heroku app.

heroku create [YOUR_PROJECT_NAME]

Stage and commit all the files.

git add .

git commit -m 'Inital commit'

And finally, push your commit to Heroku's servers.

git push heroku master

That's it! You're server-side rendered Next.js e-commerce store should be ready to go.

Live demo & GitHub repo

next-js-ecommerce-example

See the Github repo here

See the live demo here

Closing thoughts

I liked working with Next.js a lot. I was a bit skeptical at first when I read that Next.js was a framework for React considering that React is a framework in itself, but I was pleasantly surprised with how little it actually complicated things. Quite the opposite actually—it made my life easier as there was much less configuration to do.

I was also happy to know that Next.js already supports React Hooks because at the time of writing, it’s still a pretty recent addition to the React ecosystem.

To push this demo further, it would have been interesting to use dynamic imports to split the codebase into manageable chunks and fetch products from a real API rather than only returning a mocked object inside the getInitialProps function.

Are you up to it? If so, let us know how it goes in the comments below!


If you've enjoyed this post, please take a second to share it on Twitter. Got comments, questions? Hit the section below!

Suggested posts: