loading...
Cover image for Create a Next.js store with Commerce.js | Catalog pages

Create a Next.js store with Commerce.js | Catalog pages

notrab profile image Jamie Barton Updated on ・7 min read

Before you start, you'll want to create an account at Chec or use the CLI.

You'll also need to create a few categories, that have products to get the most out of this tutorial. Once you've done that, grab a copy of your public API key. You can find this at Chec Dashboard > Developer Settings.

If you don't want to create an account with Commerce.js to follow along with this tutorial, you can use the demo store public key pk_184625ed86f36703d7d233bcf6d519a4f9398f20048ec.


You can follow along with this tutorial on YouTube 📺

TLDR;


1. Initial setup

In this tutorial we will be using Next.js. To begin, inside a new directory, do the following:

npm init -y
npm install react react-dom next @chec/commerce.js
Enter fullscreen mode Exit fullscreen mode

Open package.json and add the following scripts:

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}
Enter fullscreen mode Exit fullscreen mode

Now create a new file .env and add your public API key here.

NEXT_PUBLIC_CHEC_PUBLIC_API_KEY=...
Enter fullscreen mode Exit fullscreen mode

2. Create a Commerce.js instance

With our initial Next.js setup created, and our @chec/commerce.js dependency installed, we'll now instantiate a new commerce instance, and make it available to import throughout the rest of our Next.js project.

Inside a new directory lib, create the file commerce.js. Inside here we'll export a new instance of @chec/commerce.js, following the Commerce.js Docs.

// lib/commerce.js
import CommerceSDK from "@chec/commerce.js";

const client = new CommerceSDK(process.env.NEXT_PUBLIC_CHEC_PUBLIC_API_KEY);

export default client;
Enter fullscreen mode Exit fullscreen mode

3. Create homepage of categories and products

Inside of the pages directory, we'll create the index.js file to show all our products.

While we're at it, we'll also show and link to all our categories, products, as well merchant information.

For now, let's simply show all of the information we get back from Commerce.js on our page, so we can see what we need to refactor.

Inside pages/index.js, add the following:

// pages/index.js
import commerce from "../lib/commerce";

export async function getStaticProps() {
  const merchant = await commerce.merchants.about();
  const { data: categories } = await commerce.categories.list();
  const { data: products } = await commerce.products.list();

  return {
    props: {
      merchant,
      categories,
      products,
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Here we are making use of the Next.js getStaticProps lifecylce method during build. This tells Next.js to source our content once, and return them as "static" props.

Then inside pages/index.js, let's export a default function that is our page component.

// pages/index.js
export default function IndexPage({ merchant, categories, products }) {
  return (
    <React.Fragment>
      <pre>{JSON.stringify(merchant, null, 2)}</pre>
      <pre>{JSON.stringify(categories, null, 2)}</pre>
      <pre>{JSON.stringify(products, null, 2)}</pre>
    </React.Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

4. Create ProductList & Product components

As we want to show our products in multiple places throughout this example, we should think about creating reusable React components to save us on duplicating our code.

Inside a new directory called components, create the file Product.js.

Inside here we'll export a new function, that returns the name and price.formatted_with_symbol of our products.

// components/Product.js
export default function Product({ name, price }) {
  return (
    <p>
      {name}: {price.formatted_with_symbol}
    </p>
  );
}
Enter fullscreen mode Exit fullscreen mode

Since this component only handles rendering one product, we also need to create a component that will renderer each of our Product components per product returned from Commerce.js.

Inside a new file ProductList.js in the components directory, add the following:

// components/ProductList.js
import Link from "next/link";

import Product from "./Product";

export default function ProductList({ products }) {
  if (!products) return null;

  return (
    <ul>
      {products.map((product) => (
        <li key={product.permalink}>
          <Link href={`/products/${product.permalink}`}>
            <a>
              <Product {...product} />
            </a>
          </Link>
        </li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this file we're doing a few things, so let's break it down:

  1. Importing the Link component from Next.js
  2. Importing the Product component we just created
  3. Checking if the prop products contains anything, if not, don't do anything
  4. Mapping over each of our products and invoking the Link component with Product as our child - where we spread in our product values (such as name/price) we need inside that component

5. Update index page to use ProductList component

Let's now put the ProductList component to work! Inside pages/index.js, let's add a new import right after the commerce.js client.

import ProductList from "../components/ProductList";
Enter fullscreen mode Exit fullscreen mode

Now inside the IndexPage function we can simplify replace:

<pre>{JSON.stringify(products, null, 2)}</pre>
Enter fullscreen mode Exit fullscreen mode

With our new ProductList component:

<ProductList products={products} />
Enter fullscreen mode Exit fullscreen mode

6. Create products index page

Since our index page contains the links to all our categories, products, and merchant information, we should create a page just for showing products.

Create a new file products.js inside pages directory, and add the following:

// pages/products.js
import commerce from "../lib/commerce";
import ProductList from "../components/ProductList";

export async function getStaticProps() {
  const { data: products } = await commerce.products.list();

  return {
    props: {
      products,
    },
  };
}

export default function ProductsPage({ products }) {
  return (
    <React.Fragment>
      <h1>Products</h1>

      <ProductList products={products} />
    </React.Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

In here we're importing the same components we used on the homepage but this time only requesting our products.

The benefit of creating the ProductList component once is that we can reuse it wherever we like, as long as we pass it the products prop, it'll work just fine.

7. Create CategoryList & Category components

In the same way we created the ProductList and Product components, we'll do the same for our categories.

Inside a new file Category.js inside the components directory, add the following:

// components/Category.js
export default function Category({ name }) {
  return name;
}
Enter fullscreen mode Exit fullscreen mode

We're not doing too much in this file but return the name of our category.

Next create the file CategoryList.js inside the components directory, and add the following:

// components/CategoryList.js
import Link from "next/link";

import Category from "./Category";

export default function CategoryList({ categories }) {
  if (!categories) return null;

  return (
    <ul>
      {categories.map((category) => (
        <li key={category.slug}>
          <Link href={`/categories/${category.slug}`}>
            <a>
              <Category {...category} />
            </a>
          </Link>
        </li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

The only real difference here is that we returned a list of links that go to /categories/:slug instead of /products/:permalink.

8. Create categories index page

Let's also do the same for categories. In a new file categories.js inside the pages directory, add the following:

// pages/categories.js
import commerce from "../lib/commerce";
import CategoryList from "../components/CategoryList";

export async function getStaticProps() {
  const { data: categories } = await commerce.categories.list();

  return {
    props: {
      categories,
    },
  };
}

export default function CategoriesPage({ categories }) {
  return (
    <React.Fragment>
      <h1>Categories</h1>

      <CategoryList categories={categories} />
    </React.Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

As we did in the products index page, we're simply requesting all of our categories from Commerce.js and passing them onto the CategoryList component.

9. Update index page to use CategoryList component

Now we have the CategoryList component, let's go ahead and tidy up the index page by adding this there also.

At the top of the pages/index.js file go ahead and import our new CategoryList.

import CategoryList from "../components/CategoryList";
Enter fullscreen mode Exit fullscreen mode

Then where we have:

<pre>{JSON.stringify(categories, null, 2)}</pre>
Enter fullscreen mode Exit fullscreen mode

Replace it with:

<CategoryList categories={categories} />
Enter fullscreen mode Exit fullscreen mode

While we're at it, let's also add some headings for our categories, and products with links to the pages we just created. We'll also add an h1 to the page that just shows our merchant business_name.

Also add the following import at the top of the pages/index.js file:

import Link from "next/link";
Enter fullscreen mode Exit fullscreen mode

Now update the IndexPage function to look a little something like:

// pages/index.js
export default function IndexPage({ merchant, categories, products }) {
  return (
    <React.Fragment>
      <h1>{merchant.business_name}</h1>

      <h3>
        <Link href="/categories">
          <a>Categories</a>
        </Link>
      </h3>

      <CategoryList categories={categories} />

      <h3>
        <Link href="/products">
          <a>Products</a>
        </Link>
      </h3>

      <ProductList products={products} />
    </React.Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

10. Create individual category page

Since we're linking to the individual categories inside our CategoryList component, let's go ahead and create the pages at the path /categories/:slug.

We can use Next.js to create all of our category pages at build time, and without having to create individual pages for each one too!

To do this, create a directory caled categories inside the current pages directory. Now create a new file [slug].js - including the square brackets.

This tells Next.js we want to use slug as a param to our page component.

Inside pages/categories/[slug].js we will use the same getStaticProps method to fetch and provide static props to our page. We'll retrieve an existing category from Commerce.js, and while we're at it, we'll get all of the products belonging to that category.

To do this, we can provide the current slug given to use by the current page path, to Commerce.js so we filter accordingly.

// pages/categories/[slug].js
import commerce from "../../lib/commerce";
import ProductList from "../../components/ProductList";

export async function getStaticProps({ params }) {
  const { slug } = params;

  const category = await commerce.categories.retrieve(slug, {
    type: "slug",
  });

  const { data: products } = await commerce.products.list({
    category_slug: slug,
  });

  return {
    props: {
      category,
      products,
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Now we have the API hooked up for getting data for each of our pages, we still don't have any pages.

Below the getStaticProps export, export a new function getStaticPaths.

This function must return a paths array that is the paths to our pages. We must provide that all important slug param.

// pages/categories/[slug].js
export async function getStaticPaths() {
  const { data: categories } = await commerce.categories.list();

  return {
    paths: categories.map((category) => ({
      params: {
        slug: category.slug,
      },
    })),
    fallback: false,
  };
}
Enter fullscreen mode Exit fullscreen mode

All that's left to do is export as the default our actual category page component.

// pages/categories/[slug].js
export default function CategoryPage({ category, products }) {
  return (
    <React.Fragment>
      <h1>{category.name}</h1>

      <ProductList products={products} />
    </React.Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

11. Create product page

In the same way we created our category pages, and fetched the data, we can do it for products.

Inside of a new directory products inside pages, create the file [permalink].js. We're using permalink here as that's what Commerce.js gives us for each of our products.

The contents of this file should be familiar:

// pages/products/[permalink].js
import commerce from "../../lib/commerce";

export async function getStaticProps({ params }) {
  const { permalink } = params;

  const product = await commerce.products.retrieve(permalink, {
    type: "permalink",
  });

  return {
    props: {
      product,
    },
  };
}

export async function getStaticPaths() {
  const { data: products } = await commerce.products.list();

  return {
    paths: products.map((product) => ({
      params: {
        permalink: product.permalink,
      },
    })),
    fallback: false,
  };
}

export default function ProductPage({ product }) {
  return (
    <React.Fragment>
      <h1>{product.name}</h1>
      <p>{product.price.formatted_with_symbol}</p>
    </React.Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

12. Run it locally

That's it!

Now you're ready to go! Type npm run dev in your Terminal, and head to the local port to browse your Next.js powered commerce site.


You can follow along with this tutorial on YouTube 📺

Discussion

pic
Editor guide