How to build an e-commerce website using Next.js, Xata, and Cloudinary.
Tags: nextjs, jamstack, javascript
Published: 2022-11-23 17:23:43

This article is a step-by-step guide on how to build an e-commerce website using NextJs, Xata, and Cloudinary. Xata is a serverless database; on the other hand, Cloudinary is a platform used to manage media, that is, images, videos, etc. The other technologies required to build this application include Redux Toolkit and Redux Persist for state management.

## Prerequisites
To follow along, you will need to have:
- Basic knowledge of Next JS.

## Getting started
Create a Next.js application using the following command.

```bash
npx create-next-app@latest
```

## Creating a Cloudinary account
Go to Cloudinary and sign up for an account. If you already have an account, log in. Go to the Media Library section. Click on the + icon on your top left as shown below and create a folder containing the application's images. 

![Media Library](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6sqqcnkb9a2903nbyji.PNG)

Double-click to open the folder. Next, upload images either by using the Upload button at the top right corner of the website or drag-and-drop your files if the folder is empty. 

![Upload](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zyfinrq5clv48w1pezh.PNG)

## Setting up Xata
First, create an account if you do not have one. Go to Xata and sign up or log in. Once logged in, the landing page is the workspace where you will create your databases. Click on Add a database and give it a name. For the region, you can leave the default.

![Xata Workspace](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slfqzgxeet101nsq1ftn.PNG)

![Xata workspace](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8p1e9m99wbr9yr8t4wzh.PNG)

### Creating tables
We will have two tables, namely, `product`  and `product_category`. Click Add a table on the menu on your left and add a table named product, which will contain products. 

![Add table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ukcsthdmltciqihwb24o.PNG)

This table will have the following columns; id, name, description, category, image_url, and price. You will notice that the id column is created automatically because it is a mandatory column. To create a column, click on the + icon and select the type, e.g String. 

![Create a column](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imd39a1n78r6oaf40eeb.PNG)

A pop-up will appear. Fill in the details, that is, the column name, Unique, and Not null checkboxes, and then click on create the column.

![Creating a column](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7yscvohmh730qhlylex2.PNG)

For the name column, the type will be a string and unique. The description column will be of a string type and will not be unique. The category column will be of a link type and linked to the product_category table. The image_url will be of a string type and will be unique. Lastly, the price column will be of an integer number or a decimal number type, not unique.

The product_category table will only have two columns, id, and name. The name column will be of a string type and unique.

### Adding records to the tables
Our application requires data; in this case, the data will be a list of products. In the products table, click the Add a record button, and a pop-up will appear. Fill in the details and click on the create record button once you are done.

![Add a record](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/veiwie77a0g3ayaiqckp.PNG)

A record in this database is a single product containing information such as the name, description, category, price etc, according to the columns we created earlier. Add as many records as you need. Do the same for the product_category table.

### Connecting Xata to Next.js
In your terminal, run the following command to install the Xata CLI globally.

```bash
npm i -g @xata.io/cli
```
The CLI needs to be authenticated to connect to the database using the Xata CLI. This can be done either globally or project-based. For this project, we will use project-based authentication. 

Go to the Xata Web UI and then to your workspace. Next, navigate to the configuration section and the workspace API base URL. 

![Configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bd4e7olo10djceuu3ad.PNG)

Copy the URL and replace the `{region}` and `{database}` with the region and your database name, respectively. Back in the terminal, run the following command to initialize the authentication process. Replace databaseUrl with the URL that you copied earlier.

```bash
xata init --db=databaseUrl
```
When you run this command, you will be redirected to the browser, where a new API key will be created. You will be prompted to name the API key. Once that is done, a project configuration file will be created in your current working directory, your .env file will be updated with the API key, and you will be asked if you want to install the SDK and/or use the TypeScript/JavaScript code generator. Choose the latter as it is recommended.

## Setting up Redux Toolkit and Redux Persist

We will use Redux Toolkit to manage the state of the cart in our application. On the other hand, we will use redux to persist the state of our application so that when you reload the page, the state is not lost. Run this command in your terminal to install redux toolkit and redux persist.

```bash
npm install @reduxjs/toolkit react-redux redux-persist
```

### Creating a slice
Create a folder, redux, at the root of your project folder. Next, create a file named cartSlice.js.

```javascript
    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
     cart: []
    };
    
    export const cartSlice = createSlice({
     name: 'cart',
     initialState,
     reducers: {
      addToCart: (state, action) => {
       const productExists = state.cart?.find((product) => product.id === action.payload.id);
       if(productExists) {
        productExists.quantity++;
       } else {
        state.cart.push({
         ...action.payload, quantity: 1
        })
       }
      },
      incrementQuantity: (state, action) => {
       const product = state.cart?.find((item) => item.id === action.payload.id);
       product.quantity++;
      },
      decrementQuantity: (state, action) => {
       const product = state.cart?.find((item) => item.id === action.payload.id);
       if (product.quantity === 1) {
        const index = state.cart.findIndex((item) => item.id === action.payload.id);
        state.cart.splice(index, 1);
       } else {
        product.quantity--;
       }
      },
      removeFromCart: (state, action) => {
       const index = state.cart?.findIndex((item) => item.id === action.payload.id);
       state.cart.splice(index, 1);
      }
     }
    });

    export const {
     addToCart,
     removeFromCart,
     incrementQuantity,
     decrementQuantity
    } = cartSlice.actions;

    export default cartSlice.reducer;
```

At the top of our file, we import createSlice from the redux toolkit. A slice contains the name, initialState, and reducer functions. Our slice has four reducer functions; `addToCart`, `incrementQuantity`, `decrementQuantity` and `removeFromCart`.

The addToCart function takes state and action parameters. This function checks to see if a product exists. If it exists, the quantity increases by one; if it doesn’t, the state is updated with the new product, and the quantity is one.

The incrementQuantity function increases the quantity of the product by one. 

The decrementQuantity function checks if the product quantity is equal to one. If so, the product is removed from the cart. If not, the quantity of the product is reduced by one.

The last reducer function is removeFromCart which finds the index of the product in the cart and we the splice() method to remove it.

At the bottom of the file, we export the actions and reducer to use them in other files in our application.

### Creating a store
Inside the redux folder, create a store.js file and add the following code;

```javascript
    import { configureStore } from "@reduxjs/toolkit";
    import cartReducer from './cartSlice';
    import {
     persistReducer,
     persistStore
    } from 'redux-persist';
    import thunk from "redux-thunk";
    import createWebStorage from "redux-persist/lib/storage/createWebStorage";
    
    const createNoopStorage = () => {
     return {
      getItem(_key) {
       return Promise.resolve(null);
      },
      setItem(_key, value) {
       return Promise.resolve(value);
      },
      removeItem(_key) {
       return Promise.resolve();
      },
     };
    };
    const storage = typeof window !== "undefined" ? createWebStorage("local") : createNoopStorage();
    export default storage;
    
    const persistConfig = {
     key: 'root',
     version: 1,
     storage
    };
    const persistedReducer = persistReducer(persistConfig, cartReducer);
    
    export const store = configureStore({
     reducer: persistedReducer,
     middleware: [thunk]
    });

    export const persistor = persistStore(store);
```

### Providing state to the application
We will wrap our App component with a Provider to access the state from anywhere in our application. Go to the pages folder and then _app.js, and add the following code.

```javascript
    import '../styles/global.css';
    import { Provider } from 'react-redux';
    import { store, persistor } from '../redux/store';
    import { PersistGate } from 'redux-persist/integration/react';
    
    const App = ({ Component, pageProps}) => {
     return (
      <Provider store={store}>
       <PersistGate loading={null} persistor={persistor}>
        <div className="app">
         <Component {...pageProps} />
        </div>
       </PersistGate>
      </Provider>
     )
    }
    export default App;
```

## Creating the navbar and footer components
Create a components folder at the root of your project.
Inside this folder, create these two files; Navbar.jsx and Footer.jsx
In the Navbar.jsx file, add the following code.

```javascript
    import Link from 'next/link';
    import styles from '../styles/Navbar.module.css';
    import { useSelector } from 'react-redux';
    
    const Navbar = () => {
     const cart = useSelector((state) => state.cart);
     const sumOfCartItems = () => {
      return cart?.reduce((accumulator, item) => accumulator + item.quantity, 0);
     };
     return (
      <nav className={styles.navbar}>
       <h4 className={styles.logo}>Kwetu Furniture</h4>
       <ul className={styles.navlinks}>
        <li className={styles.navlink}>
         <Link href="/">Home</Link>
        </li>
        <li className={styles.navlink}>
         <Link href="/shop">Shop</Link>
        </li>
        <li className={styles.navlink}>
         <Link href="/cart">
          <p className={styles.cart}>
           Cart <span>{sumOfCartItems()}</span>
          </p>
         </Link>
        </li>
       </ul>
      </nav>
     )
    }
    export default Navbar
```

At the top, we import the `useSelector` hook and use it later in the code to access the state in our store. This file also contains the navigation links to our pages, including Home, Shop, and Cart. For styling, we will use CSS modules. Go to the styles folder, create a file named Navbar.module.css and add the following code. The CSS file is imported in Navbar.jsx as shown above, to apply the styles.

```css
    .navbar {
     width: '100%';
     display: flex;
     justify-content: space-between;
     align-items: center;
     padding-bottom: 1rem;
    }
    .logo {
     font-size: 1.2rem;
     font-weight: 600;
     color: #654236
    }
    .navlinks {
     display: flex;
    }
    .navlink {
     list-style: none;
     margin: 0 0.75rem;
     text-transform: uppercase;
    }
    .navlink a {
     text-decoration: none;
     color: #654236;
    }
    .navlink a:hover {
     color: #f9826c;
    }
```

In the Footer.jsx file, add the following code.

```javascript
    import styles from '../styles/Footer.module.css';
    
    const Footer = () => {
     return (
      <footer className={styles.footer}>
       Kwetu &copy; 2022
      </footer>
     )
    }
    export default Footer
```

In the styles folder, create a Footer.module.css file and add these styles. This file is imported in the Footer.jsx file, as shown above.

```css
    .footer {
     text-align: center;
     color: #654236;
    }
```

Navigate to _app.js file inside the pages folder and add the following code. 

```javascript
    import Navbar from '../components/Navbar';
    import Footer from '../components/Footer';
    import '../styles/global.css';
    import { Provider } from 'react-redux';
    import { store, persistor } from '../redux/store';
    import { PersistGate } from 'redux-persist/integration/react';
    
    const App = ({ Component, pageProps}) => {
     return (
      <Provider store={store}>
       <PersistGate loading={null} persistor={persistor}>
        <div className="app">
         <Navbar />
         <Component {...pageProps} />
         <Footer />
        </div>
       </PersistGate>
      </Provider>
     )
    }
    export default App;
```

We import the Navbar and Footer components in this file to appear on all of our pages in the application. Add the following in the global.css file in the styles folder for the global styles.

```css
    @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500&display=swap');
    * {
     margin: 0;
     padding: 0;
     box-sizing: border-box;
     font-family: 'Poppins', sans-serif;
    }
    .app {
     padding: 2rem;
     min-height: 100vh;
     display: flex;
     flex-direction: column;
     justify-content: space-between;
    }
    @media only screen and (max-width: 768px) {
     .app {
      padding: 1rem;
     }
    }
```

## Creating the ProductCard component
This is a card component that will display information about a product. Inside the components folder, create a ProductCard.jsx file. Add the code below.

```javascript
    import Image from 'next/image';
    import Link from 'next/link';
    import styles from '../styles/ProductCard.module.css';
    import { useDispatch } from 'react-redux';
    import { addToCart } from '../redux/cartSlice';
    
    export const ProductCard = ({url, id, name, price, categoryName, product}) => {
     const dispatch = useDispatch();
     return (
      <div className={styles.card}>
       <Image className={styles.image}
        src={url}
        width={320}
        height={300}
       />
       <h4 className={styles.name}>{name}</h4>
       <h5 className={styles.price}>${price}</h5>
       <h6 className={styles.category}>{categoryName}</h6>
       <div className={styles.buttons}>
        <Link href={`/shop/${id}`}>
         <button className={styles.buttonone}>
          Product Details
         </button>
        </Link>
        <button
         className={styles.buttontwo}
         onClick={() => dispatch(addToCart(product))}
        >
         Add to cart
        </button>
       </div>
      </div>
     )
    };
```

This component accepts the following props; 

`URL, id, name, price, categoryName, product`

It also has a link to the Product Details page that we will create next and a button to add the product to the cart. On click of the Add to Cart button, we dispatch the addToCart action and pass the product as the payload.
To style this component, create a ProductCard.module.css file in the styles folder and add these styles.

```css
    .card {
     width: 280px;
     height: 380px;
     padding: 1rem 1.2rem;
     border-radius: 8px;
     display: flex;
     flex-direction: column;
     align-items: center;
     justify-content: space-between;
    }
    .card:hover {
     box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px;
    }
    .buttons {
     width: '100%';
     display: flex;
     justify-content: space-between;
    }
    .name {
     color: #654236;
    }
    .price {
     color: #E24E1B;
    }
    .category {
     color: #654236;
     margin-bottom: 15px;
    }
    .buttonone, .buttontwo {
     background-color: #E24E1B;
     color: white;
     padding: 10px 10px;
     border-radius: 6px;
     border: 1px solid #E24E1B;
    }
    .buttonone:hover {
     background-color: white;
     color: #E24E1B;
     border: 1px solid #E24E1B;
     cursor: pointer;
    }
    .buttonone {
     margin-right: 20px;
    }
    .buttontwo:hover {
     background-color: white;
     color: #E24E1B;
     border: 1px solid #E24E1B;
     cursor: pointer;
    }
```

## Adding pages
As mentioned earlier, our Next.js application will have three pages; Home, Shop, and Cart. We will start with creating the Home page.
Navigate to the index.js file in the pages folder and replace the boilerplate code with the following code.

```javascript 
    import Image from 'next/image';
    import Head from 'next/head';
    import Link from 'next/link';
    import styles from '../styles/Home.module.css';

    const HomePage = () => {
     return (
      <>
       <Head>
        <title>Kwetu Furniture | Home</title>
       </Head>
       <div className={styles.container}>
        <div className={styles.sectionone}>
         <h4 className={styles.title}>
          Home of elegant, stylish and affordable furniture
         </h4>
         <Link href="/shop">
          <button className={styles.button}>
           Shop Now
          </button>
         </Link>
        </div>
        <div className={styles.sectiontwo}>
         <Image
          className={styles.image}
          src={`https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/v1666527186/ecommerce/couch_yqi3f0.jpg`}
          alt="couch"
          layout='fill'
         />
        </div>
       </div>
      </>
     )
    }
    export default HomePage;
```

This page contains an image that is served from Cloudinary. To get the link to your image, go to your Cloudinary account and open the folder that contains your images. Once you have an image, you would like to use for the home page, click on the three dots and copy the URL. 

![Image URL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gzrvn1a266r5hso102yj.PNG)

The URL contains your Cloudinary name, which will be stored in a .env.local file in our Next.js app. Create a .env.local file at the root of your project. Inside this file, add the following.

`NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=yourcloudname`

Replace `yourcloudname` with your Cloudinary cloud name. 

In the index.js file, update the URL with process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME as shown below.

```javascript
<Image
 className={styles.image}
 src={`https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARCLOUD_NAME}/image/upload/v1666527186/ecommerce/couch_yqi3f0.jpg`}
 alt="couch"
 layout='fill'
/>
```
To style this page, create a Home.module.css file in the styles folder and add the styling.

```css
    .container {
     display: grid;
     grid-template-columns: 1fr 1fr;
     column-gap: 30px;
    }
    .title {
     font-size: xx-large;
     margin-bottom: 20px;
     color: #654236;
    }
    .sectionone, .sectiontwo {
     height: 70vh;
    }
    .sectionone {
     width: '100%';
     display: flex;
     flex-direction: column;
     justify-content: center;
    }
    .sectiontwo {
     position: relative;
    }
    .image {
     border-radius: 12px 12px 12px 12px;
    }
    .button {
     width: 120px;
     background-color: #E24E1B;
     color: white;
     padding: 10px 12px;
     border-radius: 6px;
     border: 1px solid #E24E1B;
    }
    .button:hover {
     background-color: white;
     color: #E24E1B;
     cursor: pointer;
     border: 1px solid #E24E1B;
    }
    @media only screen and (max-width: 768px) {
     .container {
      grid-template-columns: 1fr;
     }
    }
```

Next, create a folder named shop inside the pages folder. Inside the shop folder, create two files, index.js and [id].js. index.js is the page that will have all our products, while [id].js is a dynamic page route that will show the details of a particular product. Inside the index.js file, add the following;

```javascript
    import Head from "next/head";
    import { ProductCard } from "../../components/ProductCard";
    import styles from '../../styles/Shop.module.css';
    import { XataClient } from '../../src/xata';
    const Shop = ({ products }) => {
     return (
      <>
       <Head>
        <title>Kwetu Furniture | Shop</title>
       </Head>
       <div className={styles.container}>
        {products.map((product) => (
         <ProductCard
          product={product}
          key={product.id}
          id={product.id}
          url={product.image_url}
          name={product.name}
          price={product.price}
          categoryName={product.category.name}
         />
        ))}
       </div>
      </>
     )
    }
    const xata = new XataClient();
    export const getServerSideProps = async () => {
     const response = await xata.db.product.select(["*", "category.*"]).getAll();
     return {
      props: {
       products: response
      }
     }
    };
    
    export default Shop;
```

At the top of the file, we are importing the XataClient.

```javascript
import { XataClient } from '../../src/xata';
```

Later, we make an instance of the XataClient and assign it to a variable xata.

```javascript
const xata = new XataClient();
```

We then use the getServerSideProps function to fetch the products from our database.

```javascript
const response = await xata.db.product.select(["*", "category.*"]).getAll();
```

The code snippet above selects all columns in our product tables and all columns in the product_category table. getAll() method is one of the methods that you can use to query your data. It returns an array with all the items in our product table. If your table contains a lot of data, it is advisable to use other methods, such as getMany() or getPaginated(), which return subsets of data.

The getServerSideProps function stores the results in a variable, response, and return props. Props is an object with a key of products, and the value is response. The Shop component receives products as props. We then map through products and return a component, ProductCard, that displays the products. In the styles folder, create a Shop.module.css file and add the following styles.

```css
    .container {
     width: '100%';
     margin: 0 auto;
     display: grid;
     grid-template-columns: 1fr 1fr 1fr 1fr;
     gap: 16px;
    }
    @media only screen and (max-width: 992px) {
     .container {
      grid-template-columns: 1fr 1fr 1fr;
     }
    }
    @media only screen and (max-width: 768px) {
     .container {
      grid-template-columns: 1fr 1fr;
     }
    }
    @media only screen and (max-width: 368px) {
     .container {
      grid-template-columns: 1fr;
     }
    }
```

[id].js will have more details about the product, such as the product description. Add the code below to the file.

```javascript
    import { XataClient } from "../../src/xata";
    import Link from "next/link";
    import Image from 'next/image';
    import styles from '../../styles/ProductDetails.module.css';
    import { useDispatch } from 'react-redux';
    import { addToCart } from '../../redux/cartSlice';
    
    const ProductDetails = ({ item }) => {
     const dispatch = useDispatch();
     return (
      <div className={styles.wrapper}>
       <Link href="/shop">
        <a className={styles.link}>Go to shop</a>
       </Link>
       <div className={styles.container}>
        <Image
         src={item.image_url}
         width={400}
         height={450}
        />
        <div className={styles.details}>
         <p><span className={styles.title}>Name: </span>{item.name}</p>
         <p><span className={styles.title}>Description: </span>{item.description}</p>
         <p className={styles.price}><span className={styles.title}>Price: </span>${item.price}</p>
         <p className={styles.category}><span className={styles.title}>Category: </span>{item.category.name}</p>
         <div className={styles.button}>
          <button
           onClick={() => dispatch(addToCart(item))}
          >
           Add to cart
          </button>
         </div>
        </div>
       </div>
      </div>
     )
    }
    const xata = new XataClient();
    export const getServerSideProps = async(context) => {
     const products = await xata.db.product.select(["*", "category.*"]).getAll();
     const item = products.find((product) => product.id === context.params.id)
     return {
      props: {
       item
      }
     };
    };
    export default ProductDetails;
```

Create a ProductDetails.module.css file and add the following styles.

```css
    .title {
     font-weight: 600;
    }
    .container {
     max-width: fit-content;
     margin: 0 auto;
     display: flex;
     flex-wrap: wrap;
     gap: 18px;
     padding: 15px;
    }
    .link {
     color: #E24E1B;
     text-decoration: none;
    }
    .link:hover {
     color: #9b2b06;
    }
    .button {
     margin-top: 20px;
    }
    .button button{
     width: '60px';
     background-color: #E24E1B;
     color: white;
     padding: 10px 12px;
     border-radius: 6px;
     border: 1px solid #E24E1B;
    }
    .button button:hover {
     background-color: white;
     color: #E24E1B;
     cursor: pointer;
     border: 1px solid #E24E1B;
    }
    @media only screen and (max-width: 768px) {
     .container {
      flex-direction: column;
     }
    }
```

Lastly, we will create the Cart Page. Go to the pages folder and create a file cart.js.

```javascript
    import { useDispatch, useSelector } from "react-redux";
    import styles from '../styles/Cart.module.css';
    import Image from "next/image";
    import { incrementQuantity, decrementQuantity, removeFromCart } from "../redux/cartSlice";
    
    const Cart = () => {
     const cartList = useSelector((state) => state.cart);
     const dispatch = useDispatch();
     const totalPrice = () => {
      return cartList.reduce(
       (accumulator, item) => accumulator + item.quantity * item.price,
       0
      );
     };
     return (
      <div className={styles.container}>
       {cartList?.length === 0 ? (
        <h4>Cart is empty</h4>
       ) : (
        <>
         <div className={styles.header}>
          <div>Image</div>
          <div>Product Name</div>
          <div>Price</div>
          <div>Quantity</div>
          <div>Actions</div>
          <div>Total Price</div>
         </div>
         {cartList?.map((item) => (
          <div className={styles.body} key={item.id}>
           <div className={styles.image}>
            <Image src={item.image_url} height="90" width="65" />
           </div>
           <p>{item.name}</p>
           <p>$ {item.price}</p>
           <p>{item.quantity}</p>
           <div className={styles.buttons}>
            <button onClick={() => dispatch(incrementQuantity(item))}>
             +
            </button>
            <button onClick={() => dispatch(decrementQuantity(item))}>
             -
            </button>
            <button onClick={() => dispatch(removeFromCart(item))}>
             x
            </button>
           </div>
           <p>$ {item.quantity * item.price}</p>
          </div>
         ))}
         <h2>Grand Total: $ {totalPrice()}</h2>
        </>
       )}
      </div>
     )
    }
    export default Cart;
```

In the Cart component, we access the state of our application using the useSelector hook and store the array of products in a variable, cartList. We then use the reduce() function to calculate the total price of the products in the cart. In the JSX, we check if the length of cartList is equal to zero. If so, we return ‘Cart is empty’, if not, we map through the array and return the list of products. Each product has increment, decrement, and remove buttons.

## Conclusion
This article has covered how to set up and use Xata and Cloudinary in your Next.js e-commerce website and how to use Redux Toolkit, and Redux persist for state management. You can find more information about the different methods to query your data and additional features, such as authentication using Xata, in the [Xata documentation](https://xata.io/docs/intro/getting-started).

Below are screenshots of the final web application.

### Home page
![Home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sptcminrgu55rtd8nwf1.PNG)

### Shop
![Shop](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ysiqj2zw93tf5pqewk4x.PNG)

### Product Details
![Product Details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kbpki05pw1sabg4221s.PNG)

### Cart
![Cart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svc7ssllb3kqk6qog9j9.PNG)

Here are the links to the [deployed application](https://kwetu-furniture.netlify.app/) and the Github [repositiory](https://github.com/stephanieopala/nextjs-ecommerce).