How to Build an ecommerce site using Medusa, Next.js and Stackbit
Tags: ecommerce, medusajs, stackbit, nextjs
Published: 2023-03-08 07:54:38

## Introduction

Building modern ecommerce storefronts can be a challenge. There is a need for both technical prowess and ease of content management of the site. Developers want to build performant and secure sites, while marketers want to push sales through visually appealing storefronts. Usually, tradeoffs have to be made on either side of the equation. This is where Stackbit comes in.

![Ecommerce](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqhvopzoy9pkfdppmgfd.jpg)

In this step-by-step tutorial, you will learn how to use Stackbit and Medusa to build a custom storefront for an ecommerce website. You will learn how to use the visual building experience that Stackbit’s site builder provides to edit and build the site. Then see how the site integrates with Medusa’s REST API.

### What is Stackbit?

Stackbit is a visual experience platform that helps developers build a custom site-building experience for content managers of the site. It provides a mechanism for connecting content sources like a CMS with the frontend of a site all through a visual builder.

Here are some of the benefits of using Stackbit:

1. **Flexible development and editorial workflow**: Developers can bring their own tech stack and use their preferred component libraries and UI kits while content editors can make visual changes using a no-code approach with minimal annotations.
2. **Real-time updates**: Changes made in Stackbit or in the content source are updated instantly, providing a seamless and efficient workflow for both developers and content editors.
3. **Cloud-based visual editor**: Stackbit provides a cloud-based visual editor that picks up code changes automatically and launches an isolated container to render the site's preview.
4. **Own your content**: Stackbit does not store your content and reads and writes content directly from your API CMS or files in Git, allowing you to fully own and control your content.
5. **Stackbit is fully composable:** Stackbit works with any Node-based stack, including popular tools like Next.js, React & Tailwind, allowing for seamless integration with existing workflows and tools.

If you want to build an ecommerce storefront using Stackbit you will need a composable commerce engine to power up the tech stack. This is where Medusa comes in.

### What is Medusa?

Medusa is an open source composable commerce backend full of features to help build your ecommerce website. It has a headless server that exposes the REST APIs for your ecommerce store, an admin panel to manage your ecommerce site, and a storefront. It is fully customizable and extensible. You can build webshops, subscription services, digital products stores, ticketing systems, and any other ecommerce solutions.

Here are some of the benefits of Medusa:

1. Medusa is fully open source. No paid features or proprietary backends. No limit on the customizations, features, and integrations you can put.
2. Medusa has a wide range of ecommerce features including payment providers, cart, checkout, gift codes, multi-currency support, PIM, and many more. 
3. Medusa is composable. You can set up your store to the specifications that best suit your business needs with seamless third-party integrations.
4. Medusa is headless. You can use any frontend framework to interact with it.
5. Medusa offers community support despite being free and open source. Through their Discord channel and GitHub discussions, you will have enough help to get past any bottleneck.

### Resources

Here is a demo of the website you will build.

![Ecommerce website demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5b0sbiifmms3pgkm355.gif)

{% embed https://youtu.be/vpITpBFqmcU %}

- [Source code](https://github.com/Marktawa/medusa-stackbit-app)

## Prerequisites

- [Node.js (LTS)](https://nodejs.org/en/download/) - ************v14 or later************
- [Git](https://git-scm.com/downloads)
- [Stackbit account](https://app.stackbit.com/register)

## Medusa Server Setup

Medusa provides the ecommerce backend for the storefront you will create. Let’s get the server up and running.

### Install Medusa CLI tool

Install the Medusa CLI tool globally on your work machine.

```bash
npm install @medusajs/medusa-cli -g
```

### Install Medusa server

Install the Medusa server in your working directory.  Name it `medusa-store` and seed the server with data.

```bash
medusa new medusa-store --seed
```

### Configure CORS Settings

In your text editor open up `medusa-store/medusa-config.js` file and update it to allow the Stackbit client to consume the Medusa Store API.

```jsx
...
// CORS to avoid issues when consuming Medusa from a client
const STORE_CORS = process.env.STORE_CORS || "http://localhost:8000,http://localhost:3000,http://localhost:8090";
...
```

### Start Medusa server

```bash
cd medusa-store
medusa develop
```

Your Medusa server is now running on port 9000. 

### Test Medusa server

```bash
curl localhost:9000/store/products
```

> ****OPTIONAL**:*
***You can also visit localhost:9000/store/products in your browser to test your Medusa server.***
> 

The response from the Medusa server should be a large JSON payload. Here are the first few lines of the response:

```jsx
{
    "products": [
        {
            "id": "prod_01GQTERP2S14W3MJFYAMG7AB7S4",
            "created_at": "2023-02-04T08:35:19.000Z",
            "updated_at": "2023-02-04T08:35:19.000Z",
            "deleted_at": null,
            "title": "Medusa Coffee Mug",
            "subtitle": null,
            "description": "Every programmer's best friend.",
            "handle": "coffee-mug",
						...
```

The [Medusa Quickstart](https://docs.medusajs.com/quickstart/quick-start/) guide has more information related to setting up your server.

## Next.js Starter Setup

The ecommerce backend is up and running. Next, you will set up the frontend for your ecommerce store. For the frontend, we will use [Stackbit’s Next.js Starter](https://github.com/stackbit-themes/nextjs-starter) and turn it into a storefront.

It is a nice minimal starting point for you to modify into a storefront. It is already [configured to work with Stackbit](https://docs.stackbit.com/getting-started/files/setup#configure-stackbit).

### Install Starter

In a new working directory separate from `medusa-store`, create a project for the starter. 

```bash
npx create-stackbit-app@latest --starter nextjs
```

This creates a new repo in a `my-stackbit-site` folder and installs the dependencies for the starter.

### Folder Structure

Take a look at the directory structure of the Next.js starter project folder.

```bash
cd my-stackbit-site
tree -L 1 -a
```

Here’s a top level view.

```bash
.
├── components
├── content
├── .eslintrc.json
├── .git
├── .gitignore
├── LICENSE
├── netlify.toml
├── .next
├── next.config.js
├── node_modules
├── .nvmrc
├── package.json
├── package-lock.json
├── pages
├── .prettierrc
├── public
├── README.md
├── .stackbit
├── stackbit.config.js
├── styles
└── utils
```

- The `components` folder contains the components that you use to build the pages.
- The `content` folder contains the content (markdown files) for the starter.
- The `pages` folder has the `[[...slug]].js` file which is a catch-all page template that maps content to the specific component as defined in the `components` directory.
- The `.stackbit` folder has the `models` folder which contains models which provide the schema definition for Stackbit to understand your content
- The `stackbit.config.js` file enables you to customize the visual editing experience of your site. It is already set with the minimal configuration for the Next.js site.
- The `utils` folder has a `content.js` file which contains a set of utility functions for reading and parsing the content files in the `content` folder.

### Launch the site

Run the Next.js development server in your starter project folder.

```bash
npm run dev
```

This starts up your Next.js development server on port 3000.

Visit `localhost:3000` in your browser to see the home page.

![Stackbit Next.js Starter Home Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/99hjph5zlr4rk3smfu62.png)

## Stackbit Setup

Stackbit uses a content-driven architecture where content and code are separate from each other. To set up a local development server for Stackbit you need to install the Stackbit CLI. You will then use the `stackbit dev` command to run the development environment. 

The Stackbit development server runs in parallel with the Next.js development server to create the visual editing environment.

### Install Stackbit CLI

```bash
npm install -g @stackbit/cli
```

### Run Stackbit Dev

Ensure your Next.js development server is still running on port `3000`. Navigate to `my-stackbit-site` and start the Stackbit development server.

```bash
cd my-stackbit-site
stackbit dev
```

Both servers should be running in parallel. The Stackbit server will be running on port 8090 start forwarding requests to `http://localhost:3000`. Open `http://localhost:8090/_stackbit` in your web browser and register or sign in to your Stackbit account.

After signing in to your account, you should be met with Stackbit’s visual editor containing your Next.js project.

![Stackbit Visual Editor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hyeefwcue0zbhlcohs1l.png)

## Editing Content using Stackbit

### **Add Header**

We are going to start editing content using Stackbit by doing a preliminary setup first. You will first add a model for the Header.

In a new terminal session in the `my-stackbit-site` directory, create a new file named `.stackbit/models/HeaderConfig.js`

```bash
cd my-stackbit-site
touch .stackbit/models/HeaderConfig.js
```

In your text editor, add the following code to your header model.

```jsx
module.exports = {
    label: 'Header Config',
    labelField: 'title',
    fields: [
        { type: 'string', name: 'title', label: 'Title', default: 'Your Brand' },
        {
            type: 'list',
            name: 'navLinks',
            label: 'Navigation Links',
            items: {
                type: 'object',
                fields: [
                    { type: 'string', name: 'label', label: 'Label' },
                    { type: 'string', name: 'url', label: 'URL' },
                ],
            },
        },
    ],
  };
```

This tells Stackbit that the Header component will have two fields: `title` for the website’s title to be displayed on the Header and `navLink` for the links to the pages in the website.

Add a component for the Header.

```bash
touch components/Header.jsx
```

In your text editor, add the following code to your Header component.

```jsx
// components/Header.jsx
export const Header = ({ siteConfig }) => {
    const headerObjectId = siteConfig.__id + ':header';
    return (
        <header className="header outer" data-sb-field-path={headerObjectId}>
            <div className="inner">
                <span className="header-title" data-sb-field-path=".title">{siteConfig.header.title}</span>
                <span className="header-nav" data-sb-field-path=".navLinks">
                    {siteConfig.header.navLinks.map((navLink, idx) => (
                        <a href={`${navLink.url}`} data-sb-field-path={`.${idx}`}>{navLink.label}</a>
                    ))}
                </span>
            </div>
        </header>
    );
};
```

This will render a header with the site title and navigation links to pages. The `data-sb-field-path` attribute is used to enable inline editing for the elements in the header.

Next, edit `SiteConfig.js` in the `.stackbit/models` directory, to make the Header component accessible to all pages of the website.

```jsx
// .stackbit/models/SiteConfig.js
module.exports = {
  type: 'data',
  label: 'Site Config',
  singleInstance: true,
  fields: [
    { type: 'string', name: 'title', label: 'Site Title' },
    {
      type: 'model',
      name: 'footer',
      label: 'Footer Config',
      models: ['FooterConfig'],
    },
    {
      type: 'model',
      name: 'header',
      label: 'Header Config',
      models: ['HeaderConfig'],
    },
  ],
};
```

Import your Header model into the `stackbit.config.js` file in the root of your `my-stackbit-site` directory. This is the main configuration file for Stackbit.

```jsx
// stackbit.config.js
import Button from './.stackbit/models/Button';
import Card from './.stackbit/models/Card';
import Page from './.stackbit/models/Page';
import CardGridSection from './.stackbit/models/CardGridSection';
import FooterConfig from './.stackbit/models/FooterConfig';
import HeroSection from './.stackbit/models/HeroSection';
import SiteConfig from './.stackbit/models/SiteConfig';
import HeaderConfig from './.stackbit/models/HeaderConfig';

const sbConfig = {
  stackbitVersion: '~0.6.0',
  ssgName: 'nextjs',
  cmsName: 'git',
  nodeVersion: '16',
  dataDir: 'content/data',
  pagesDir: 'content/pages',
  pageLayoutKey: 'type',
  assets: {
    referenceType: 'static',
    staticDir: 'public',
    uploadDir: 'images',
    publicPath: '/',
  },
  models: {
    Page,
    Button,
    Card,
    CardGridSection,
    FooterConfig,
    HeroSection,
    SiteConfig,
    HeaderConfig,
  },
};

export default sbConfig;
```

Add sample data to appear on your Header using the `content/data/config.json` file. Give your site the `title` : “Medusa + Stackbit” and add the `navLinks` for `Home` and the `Store` page.

```jsx
{
    "type": "SiteConfig",
    "title": "Stackbit",
    "footer": {
        "type": "FooterConfig",
        "body": "Made by [Stackbit](https://www.stackbit.com/)\n"
    },
    "header": {
        "type": "HeaderConfig",
        "title": "Medusa + Stackbit",
        "navLinks": [
            {
                "label": "Home",
                "url": "/"
            },
            {
                "label": "Store",
                "url": "/store"
            }
        ]
    }
}
```

Update the `pages/[[...slug]].js` file to render the Header when you load your Stackbit site.

```jsx
// pages/[[...slug]].js
import Head from 'next/head';

import { DynamicComponent } from '../components/DynamicComponent';
import { Footer } from '../components/Footer';
import { Header } from '../components/Header';
import { pagesByType, siteConfig, urlToContent } from '../utils/content';

const FlexiblePage = ({ page, siteConfig }) => {
  return (
    <div className="page">
      <Head>
        <title>{page.title}</title>
      </Head>
      <Header siteConfig={siteConfig} />
      <div data-sb-object-id={page.__id}>
        {page.sections?.length > 0 && (
          <div data-sb-field-path="sections">
            {page.sections.map((section, index) => (
              <DynamicComponent
                key={index}
                {...section}
                data-sb-field-path={`.${index}`}
              />
            ))}
          </div>
        )}
      </div>
      <Footer siteConfig={siteConfig} />
    </div>
  );
};

export default FlexiblePage;

export function getStaticProps({ params }) {
  const url = '/' + (params.slug || []).join('/');
  return { props: { page: urlToContent(url), siteConfig: siteConfig() } };
}

export function getStaticPaths() {
  const pages = pagesByType('Page');
  return {
    paths: Object.keys(pages),
    fallback: false,
  };
}
```

Add some styling to your header by adding the following lines at the end of `styles/styles.css`.

```css
/* ---- Header ---- */
.header {
  background: var(--color-blue);
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--color-white);
  padding: 2rem 0rem;
}

.header-nav {
  float: right;
}

.header-nav a {
  padding-right: 1rem;
}

.header-nav a:hover {
  color: var(--color-yellow);
}
```

Test out your header page. It should be similar to this:

![Header for Medusa + Stackbit site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7n67e9h7j3r6p3lxlke.png)

### **Edit Hero Section**

Now, that you have seen how to set up an editable component for your content editor, the next step is to edit the Hero section to see inline editing in action. 

Change the message “Welcome to Stackbit” to “Welcome to Medusa + Stackbit!” in the Hero section.

![Edit Hero for Medusa + Stackbit Site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmlfkz2hrk1ox5qm56lo.png)

This is the ease with which you can visually edit the content on your site without tinkering with any code.

### Add Store Page

Next, let’s add a store page to retrieve and display products from our Medusa ecommerce store. Notice that the `Store` link in the header of your site does not work since we have yet to create a `Store` page.

Click on the `Add New Page`*(white and blue `+`)* icon at the top of your visual editor. Give your page the title `Store` and Slug `/store` and save by clicking `+ Add`. This will create a blank page with the title Store. 

![Add New Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tplvpeqtz2yezh6wc0zk.png)

If you now click `Store` on the header it will navigate to the blank page.

### Add Model for AllProducts

To add functionality to your `Store` page, you will need to create a model called `AllProducts.js` in the `.stackbit/models` directory. This provides the structure for the content to be displayed on the `Store` page.

In your terminal create `AllProducts.js`.

```bash
touch .stackbit/models/AllProducts.js
```

In your text editor, add the following code to `AllProducts.js`

```jsx
// stackbit/models/AllProducts.js
module.exports = {
    label: 'All Products',
    groups: ['SectionComponents'],
    fields: [
        { type: 'string', name: 'heading', default: 'All Products' },
        {
            name: 'products',
            type: 'list',
            items: {
                type: 'object',
                fields: [
                    { type: 'string', name: 'name' },
                    { type: 'string', name: 'id' },
                    { type: 'string', name: 'description' },
                    { type: 'string', name: 'price' },
                ],
            },
        },
    ],
}
```

For simplicity, the AllProducts model has two fields of interest: a `heading` for the `Store` page heading and array of `products`.

Import the `AllProducts.js` model you just created into your `stackbit.config.js` file. The updated `stackbit.config.js` file should be as follows:

```jsx
// stackbit.config.js
import Button from './.stackbit/models/Button';
import Card from './.stackbit/models/Card';
import Page from './.stackbit/models/Page';
import CardGridSection from './.stackbit/models/CardGridSection';
import FooterConfig from './.stackbit/models/FooterConfig';
import HeroSection from './.stackbit/models/HeroSection';
import SiteConfig from './.stackbit/models/SiteConfig';
import AllProducts from './.stackbit/models/AllProducts';
import HeaderConfig from './.stackbit/models/HeaderConfig';

const sbConfig = {
  stackbitVersion: '~0.6.0',
  ssgName: 'nextjs',
  cmsName: 'git',
  nodeVersion: '16',
  dataDir: 'content/data',
  pagesDir: 'content/pages',
  pageLayoutKey: 'type',
  assets: {
    referenceType: 'static',
    staticDir: 'public',
    uploadDir: 'images',
    publicPath: '/',
  },
  models: {
    Page,
    Button,
    Card,
    CardGridSection,
    FooterConfig,
    HeroSection,
    SiteConfig,
    AllProducts,
    HeaderConfig,
  },
};

export default sbConfig;
```

Next, create an `AllProducts` component that uses the `AllProducts` model in the `components` folder. This component will fetch all the products from the Medusa Server and display them on the `Store` page.

```bash
touch components/AllProducts.jsx
```

Add the following code to AllProducts.jsx

```jsx
// components/AllProducts.jsx
import { useState, useEffect } from "react";
import Link from 'next/link';

export const AllProducts = (props) => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const fetchProducts = async () => {
      const response = await fetch('http://localhost:9000/store/products');
      const data = await response.json();
      setProducts(data.products);
    };
    fetchProducts();
  }, []);

  return (
    <div data-sb-field-path={props['data-sb-field-path']}>
      <div className="outer heading-box">
        <div className="inner heading-box">
          <h1 className="store-heading" data-sb-field-path=".heading">{props.heading}</h1>
        </div>
      </div>
      <div className="outer store-box">
        <div className="inner store-box">
          <ul className="store-list">
            {products.map((product) => (
              <li key={product.id}>
                <Link href={`/store/${product.id}`}>
                  <h2>{product.title}</h2>
                  <img src={`${product.thumbnail}`} width="250"></img>
                  <p>${product.variants[0].prices[0].amount}</p>
                </Link>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
};
```

This component retrieves the products from the Medusa Server at `localhost:9000/store/products` and updates the `products` state variable with the fetched products. The products are then rendered with a link to each individual product’s page which we will need to create.

To make the `AllProducts` component function, you must import it into the `DynamicComponent` component. Update `DynamicComponent` as follows:

```jsx
// components/DynamicComponent.jsx
import { HeroSection } from './HeroSection';
import { CardGridSection } from './CardGridSection';
import { AllProducts } from './AllProducts';

const componentsMap = {
  HeroSection: HeroSection,
  CardGridSection: CardGridSection,
  AllProducts: AllProducts,
};

export const DynamicComponent = (props) => {
  if (!props.type) {
    const propsOutput = JSON.stringify(props, null, 2);
    throw new Error(`Object does not have a 'type' property: ${propsOutput}`);
  }

  const Component = componentsMap[props.type];
  if (!Component) {
    throw new Error(`No component is registered for type:'${props.type}`);
  }
  return <Component {...props} />;
};
```

Add the styling for the `Store` page. Add the following lines at the end of `styles/style.css`.

```css
/* ---- Store ---- */
.outer.heading-box {
  background-image: url("/bg.svg");
  background-repeat: repeat;
  background-color: rgb(193, 189, 189);
  padding: 5rem 0rem;
}

.inner.heading-box {
  text-align: center;
  font-size: 2.4rem;
  font-weight: 800;
  color: rgb(4, 4, 83);
}

.inner.store-box {
  text-align: center;
  padding: 1rem 0rem;
}

ul.store-list {
  list-style: none;
}

ul.store-list p {
  font-size: 1.5rem;
  color: darkblue;
}
```

Now click on the `Store` page link on the site header. You should see a list of all products from the Medusa Server.

![Store Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tv86i9nxuyby5k6b659.png)

The `Store` page loads well with a list of all products. However, clicking on each product gives a `404` Error.

### Add Single Product Page

To create individual product pages for each product, we will use Next.js’s dynamic routing feature.

Create a new directory named `store` in the `pages` directory of your `my-stackbit-site` folder. In that directory create a new page file called `[productId].jsx`. This will be a template for the individual product pages.

```bash
mkdir pages/store && touch pages/store/[productId].jsx
```

Add the following code to `productId.jsx` :

```jsx
// pages/store/[productId].jsx
import { useRouter } from 'next/router';
import { useState, useEffect } from 'react';

const ProductPage = () => {
  const router = useRouter();
  const { productId } = router.query;
  const [product, setProduct] = useState(null);

  useEffect(() => {
    const fetchProduct = async () => {
      const response = await fetch(`http://localhost:9000/store/products/${productId}`);
      const data = await response.json();
      setProduct(data.product);
    };
    fetchProduct();
  }, [productId]);

  if (!product) {
    return <div>Loading...</div>;
  }

  return (
    <div className="outer product-box">
      <div className="inner product-box">
        <h1>{product.title}</h1>
        <img src={`${product.thumbnail}`} width="320"></img>
        <p className="description">{product.description}</p>
        <p className="price">${product.variants[0].prices[0].amount}</p>
      </div>
    </div>
  );
};

export default ProductPage;
```

The `useRouter` hook is used to get the `id` of the product from the URL, then uses that `id` to fetch data for the individual product. When a user clicks on a product link on the `Store` page, they will be taken to an individual product page with the corresponding data fetched from the Medusa server.

Next, add some styling to your `ProductPage` by adding the following CSS code at the end of the `styles/style.css` file.

```css
/*---- Individual Product ----*/
.inner.product-box {
  text-align: center;
  padding: 3rem 1rem;
}

.price {
  color: darkblue;
  font-size: 1.5rem;
}
```

Preview the `product` page by clicking on one of the products in the `Store` page.

![Product Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/24qbm6teua01xhzamx4n.png)

### Add Cart

The Medusa backend can handle the creation and storage of the cart, the retrieval of the cart, adding, updating, and deleting line items from the cart. This frees frontend developers from having to write lots of logic to manage the state of your cart. Check out [How to Add Cart functionality | Medusa](https://docs.medusajs.com/guides/carts-in-medusa/) to learn more about carts in Medusa.

For this tutorial, we will just be implementing the creation of the cart and adding line items to the cart.

First, add a link to the `Cart` in the header by updating the `content/data/config.json` file.

```json
{
    "type": "SiteConfig",
    "title": "Stackbit",
    "footer": {
        "type": "FooterConfig",
        "body": "Made by [Stackbit](https://www.stackbit.com/)\n"
    },
    "header": {
        "type": "HeaderConfig",
        "title": "Medusa + Stackbit",
        "navLinks": [
            {
                "label": "Home",
                "url": "/"
            },
            {
                "label": "Store",
                "url": "/store"
            },
            {
                "label": "Cart",
                "url": "/cart"
            }
        ]
    }
}
```

If you refresh your site, you should see a link to the `Cart` in the page header. 

![Link to Cart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s20gukdny0zyccxabwpg.png)

Next, create the `Cart` page which will create a new cart or retrieve a pre-existing cart from `localStorage`, display the list of products in the cart, and has an `addProductToCart` function for adding new line items to the cart.

Create a new file named `cart.js` in the `pages` folder.

```bash
touch pages/cart.js
```

Add the following code to `pages/cart.js` :

```jsx
// pages/cart.js
import React, { useState, useEffect } from "react";

export default function Cart() {
  const [cart, setCart] = useState(null);

  useEffect(() => {
    const id = localStorage.getItem("cart_id");
    if (id) {
      fetch(`http://localhost:9000/store/carts/${id}`, {
        credentials: "include",
      })
        .then((response) => response.json())
        .then(({ cart }) => setCart(cart))
    } else {
      fetch(`http://localhost:9000/store/carts`, {
        method: "POST",
        credentials: "include",
      })
        .then((response) => response.json())
        .then(({ cart }) => {
          setCart(cart);
          localStorage.setItem("cart_id", cart.id);
        });
    }
  }, []);

  if (!cart) {
    return <div>Loading Cart...</div>;
  }

  return (
    <>
     <div className="outer cart-box">
          <div className="inner cart-box">
              <h2>Your Cart</h2>
              <table>
                  <thead>
                      <tr>
                          <th>Item</th>
                          <th>Unit Price</th>
                          <th>Quantity</th>
                      </tr>
                  </thead>
                  <tbody>
                      {cart.items.map((item) => (
                          <tr key={item.id}>
                              <td>{item.title}</td>
                              <td>${item.unit_price}</td>
                              <td>{item.quantity}</td>
                          </tr>
                      ))}
                  </tbody>
              </table>
          </div>
      </div>
    </>
  );
};

export const addProductToCart = (variant_id) => {
  const id = localStorage.getItem("cart_id");
  if (id) {
    fetch(`http://localhost:9000/store/carts/${id}/line-items`, {
      method: "POST",
      credentials: "include",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        variant_id,
        quantity: 1,
      }),
    })
      .then((response) => response.json())
      .then(({ cart }) => console.log(cart));
  }
};
```

This is a simple cart to illustrate the power of Medusa’s ecommerce backend. The `useEffect` is used to fetch a cart using the `id` of the cart, if it doesn’t exist it will create one and parse it to `cart` array. The cart is then rendered with the list of products.

Next, add some styling to the `cart` page by adding the following code at the end of `styles/style.css`

```css
/*---- Cart ----*/
.outer.cart-box {
  padding: 2rem 1rem;
}

.cart-box li {
  list-style: none;
  padding-bottom: 1rem;
}

/*---- Table ----*/
table {
  border-collapse: collapse;
  border: 3px solid rgb(200,200,200);
  letter-spacing: 1.5px;
  font-size: 1rem;
}

td, th {
  border: 1px solid rgb(190,190,190);
  padding: 10px 20px;
}

th {
  background-color: rgb(235,235,235);
}

td {
  text-align: center;
}

tr:nth-child(even) td {
  background-color: rgb(250,250,250);
}

tr:nth-child(odd) td {
  background-color: rgb(245,245,245);
}

caption {
  padding: 10px;
}
```

To test out the cart, you will need to import the `addProductToCart` function into the `AllProducts` component.

Update the `AllProducts` component with the following code:

```jsx
// components/AllProducts.jsx
import { useState, useEffect } from "react";
import Link from 'next/link';
import { addProductToCart } from '../pages/cart';

export const AllProducts = (props) => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const fetchProducts = async () => {
      const response = await fetch('http://localhost:9000/store/products');
      const data = await response.json();
      setProducts(data.products);
    };
    fetchProducts();
  }, []);

  return (
    <div data-sb-field-path={props['data-sb-field-path']}>
      <div className="outer heading-box">
        <div className="inner heading-box">
          <h1 className="store-heading" data-sb-field-path=".heading">{props.heading}</h1>
        </div>
      </div>
      <div className="outer store-box">
        <div className="inner store-box">
          <ul className="store-list">
            {products.map((product) => (
              <li key={product.id}>
                <Link href={`/store/${product.id}`}>
                  <h2>{product.title}</h2>
                  <img src={`${product.thumbnail}`} width="250"></img>
                  <p>${product.variants[0].prices[0].amount}</p>
                </Link>
                <button className="button" onClick={() => { 
                  addProductToCart(product.variants[0].id); 
                  alert('Added to Cart');
                  }}>Add to Cart</button>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
};
```

We have added an `Add to Cart` button which implements the `addProductToCart` function for each product. Reload your `Store` page, to see the changes take effect.

![Add to Cart Button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y5fethu2vme8z81tx6jl.png)

Click on the `Add to Cart` button for any product, then navigate to the `Cart` page and you will find the items you just added to your cart list. Here’s an example:

![Cart page with added products](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uea9mt8q0hwjpxq6q6bh.png)

## Preview Site

After adding all these features, the next step is to preview your site. Stackbit provides a `Preview` option in the Visual Content Editor for you to test out the site.

![Stackbit Preview feature](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wem39ayvqhepqkxtmgdt.png)

Feel free to toggle between the `Preview` button and the `Edit` button and tinker with your site. 

## Deployment

The next step is the process of deployment. For in-depth guides on deploying the Medusa server use [Medusa Server Deployment Guides](https://docs.medusajs.com/deployments/server/). 

This tutorial did not cover creating a Stackbit cloud project. This is useful when you want to introduce content editors to your site. Check out [Create Stackbit from GitHub](https://docs.stackbit.com/features/import) for more information.

As for deploying your storefront, you can use Vercel or Netlify. Check out these articles:

- [How to Deploy Your Medusa Ecommerce Storefront and Admin from a Monorepo using Vercel](https://medium.com/medusa-commerce/how-to-deploy-your-medusa-ecommerce-storefront-and-admin-from-a-monorepo-using-vercel-8d3d4bb7019b)
- [Medusa Storefront Deployment Guides](https://docs.medusajs.com/deployments/storefront/)

## Conclusion

In this tutorial, we learned how to create an e-commerce storefront using Medusa and Stackbit. 

We explored the visual editing experience that Stackbit provides as well as the feature-rich Medusa ecommerce backend.

We added a Store page that displays all products from the Medusa server and added individual product pages for each product using Next.js dynamic routing. We also added a simple cart that displays the list of products. Finally, we previewed the site and discussed deployment options.

As you continue to work with Stackbit and Medusa, there are many additional features and customizations that you can explore. For example, you can add more [complex cart and checkout functionality](https://docs.medusajs.com/advanced/storefront/how-to-implement-checkout-flow/), integrate payment providers and create a custom design for your storefront.

It's important to remember that this tutorial only scratched the surface of what is possible with these powerful tools. However, it should provide you with a solid foundation to build upon and experiment with.

If you run into any issues or have questions, feel free to reference the extensive documentation available for both Stackbit and Medusa or to seek help from the vibrant communities surrounding these tools.

Happy building!