Create your ecommerce Website using React.js + Strapi + SnipCart in under an hour

Create your ecommerce Website using React.js + Strapi + SnipCart in under an hour

In this tutorial you will be able to launch your online store in under an hour using the latest technologies : React.js , Strapi and SnipCart .


Here is the main sections of this tutorial :

Sections :

  1. Build your Backend using Strapi
  2. Create a React.js app to display products
  3. Create a SnipCart account & integrate it in your React.js app

#1# Build your Backend using Strapi :

  1. What is Strapi ?

Strapi is an open source Headless CMS that gives you the ability to create your models quickly and  generate the admin panel in a few clicks where you get your whole CMS setup in a few minutes.

Strapi offers you the possibility to fetch any data you might need via a REST API or GraphQL endpoint. That‘s Why Strapi !

  1. Setup Strapi :

Using Strapi you have the possibility to use MySql , Postgresql , Sqlite or mongodb . In this tutorial I will setup Strapi with a postgresql database .

So let’s get started !

Strapi Documentation
The headless CMS developers love.

To install strapi make sure you meet these requirements :

Node.js : only LTS versions are supported (v12 and v14)
npm 6.x

  1. create strapi project and choose postgresql
npx create-strapi-app my-project

At this stage , don’t panic , we are going to create our postgresql database , all what you have to do is to create a new heroku app + add a postgresql addon (start free ) :

Now you can go and get your database credentials

Then , go back to your terminal and add what you have got :

If you run into an issue , you can try creating a quickstart strapi project and then replace your database config file (config/database.js) with this :

module.exports = ({ env }) => ({
"defaultConnection": "default",
"connections": {
"default": {
"connector": "bookshelf",
"settings": {
"client": "postgres",
"host": "YOUR_HOST",
"port": "5432",
"database": "YOUR_DATABASE_NAME",
"username": "YOUR_USERNAME",
"password": "************************************",
"ssl": { "rejectUnauthorized": false }
},
"options":{
"pool":{
"min":0,
"max":20,
"idleTimeoutMillis":30000,
"createTimeoutMillis":30000,
"acquireTimeoutMillis":30000
}
}
}
}
}
);

additionally run this command to install postgresql package :

npm install pg --save

2 . Now , go to your admin dashboard :  localhost:1337/admin

3 . The next step is to create a content-type which is our product model , in this example I will define a product by : title , description , image , price and quantity .

4 . Then , go and add your products :

Everything is settled , the last thing we must do is to turn the product’s route permissions to public : ( count , find , findOne )

5 . Testing our API :

Congrats ! now time to create our react.js app .


#2# Create a React.js app to display products :

I will be using this project of AlexSegen :

https://github.com/AlexSegen/react-shopping-cart

You can create your own design , but if you are going to use the same project as mine , you have to modify some files , to simplify the job I will leave you my own version here .

[change the react_app_host environment variable to your strapi url ]

Otherwise , all you have to do is to fetch over your API .

  1. Product context :

import React, { createContext, useState , useEffect } from 'react';

export const ProductsContext = createContext()

const ProductsContextProvider = ({children}) => {

const [hasError, setErrors] = useState(false);

const [products, setProducts] = useState(null);

const fetchData = async () => {

const res = await fetch(`${process.env.REACT_APP_HOST}/products`);
res
.json()
.then((res) =>{
setProducts(res)
console.log("success",res)
})
.catch((err) => setErrors("error :",err));

}

useEffect(() => {
fetchData();
}, []);

return (
<>
{ products && <ProductsContext.Provider value={{products}} >
{ children }
</ProductsContext.Provider>
}
</>
);
}

export default ProductsContextProvider;

2. Cart Item :

import React, { useContext } from 'react';
import { PlusCircleIcon, MinusCircleIcon, TrashIcon } from '../../components/icons'
import { CartContext } from '../../contexts/CartContext';

import { formatNumber } from '../../helpers/utils';

const CartItem = ({product}) => {

const { increase, decrease, removeProduct } = useContext(CartContext);

return (
<div className="row no-gutters py-2">
<div className="col-sm-2 p-2">
<img
alt={product.title}
src={`${process.env.REACT_APP_HOST}${product.image.url}`} className="img-fluid d-block"/>
</div>
<div className="col-sm-4 p-2">
<h5 className="mb-1">{product.name}</h5>
<p className="mb-1">Price: {formatNumber(product.price)} </p>

</div>
<div className="col-sm-2 p-2 text-center ">
<p className="mb-0">Qty: {product.Qte}</p>
</div>
<div className="col-sm-4 p-2 text-right">
<button
onClick={() => increase(product)}
className="btn btn-primary btn-sm mr-2 mb-1">
<PlusCircleIcon width={"20px"}/>
</button>

{
product.quantity > 1 &&
<button
onClick={() => decrease(product)}
className="btn btn-danger btn-sm mb-1">
<MinusCircleIcon width={"20px"}/>
</button>
}

{
product.quantity === 1 &&
<button
onClick={() => removeProduct(product)}
className="btn btn-danger btn-sm mb-1">
<TrashIcon width={"20px"}/>
</button>
}

</div>
</div>
);
}

export default CartItem;

3. Product item


import React, { useContext } from 'react';
import { Link } from 'react-router-dom';
import { CartContext } from '../../contexts/CartContext';
import { formatNumber } from '../../helpers/utils';

const ProductItem = ({product}) => {

const { addProduct, cartItems, increase } = useContext(CartContext);

const isInCart = product => {
return !!cartItems.find(item => item.id === product.id);
}

return (
<div className="card card-body">
<img style={{display: "block", margin: "0 auto 10px", maxHeight: "200px"}} className="img-fluid"
src={`${process.env.REACT_APP_HOST}${product.image.url}`} alt=""/>
<p>{product.title}</p>
<h3 className="text-left">{formatNumber(product.price)}</h3>
<div className="text-right">
<Link  to="/" className="btn btn-link btn-sm mr-2">Details</Link>

{
isInCart(product) &&
<button
onClick={() => increase(product)}
className="btn btn-outline-primary btn-sm">Add more</button>
}

{
!isInCart(product) &&
<button
onClick={() => addProduct(product)}
className="btn btn-primary btn-sm">Add to cart</button>
}

</div>
</div>
);
}

export default ProductItem;


4. Final View :

Here is the final view of the website :

The last step is adding the checkout configurations , but before , you must deploy the strapi project to heroku using the app created in section 1 .


#3# Create a SnipCart account & integrate it in your React.js app :

1.What is Snipcart ?

Snipcart is a third party shopping cart which can be integrated easily with any framework or CMS .

2.Why Snipcart ?

When you want to build a fast e-commerce solution to save time and focus on other features I highly recommend you to use snipcart , you can see  the main differences and compare it with other existing third parties in this link .

3.How to integrate Snipcart in this project ?

#Step 1 :

First you need to create a snipcart account and turn on the test mode .

#Step 2 (Optional) :

Let snipcart know your allowed domain name so it can be shown while sending invoices …

#Step 3 :

Check the features you need on snipcart :

#Step 4 :

We need to get our API key to add it on our index.html page , you can find it on API keys section as shown below :

After , add this lines into public/index.html in your react app :

<script async src="https://cdn.snipcart.com/themes/v3.0.22/default/snipcart.js"></script>
<div id="snipcart" data-api-key="YOUR_PUBLIC_KEY" hidden></div>

to use snipcart’s style :

<link rel="preconnect" href="https://app.snipcart.com">
<link rel="preconnect" href="https://cdn.snipcart.com">

<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.0.22/default/snipcart.css" />

<link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.0.22/default/snipcart.css" />

#Step 5 :

Now back to our react components , all we need is to add snipcart-* classNames and some tags to our product item component so snipcart can process them :


import React, { useContext } from 'react';
import { Link } from 'react-router-dom';
import { CartContext } from '../../contexts/CartContext';
import { formatNumber } from '../../helpers/utils';

const ProductItem = ({product}) => {

const { addProduct, cartItems, increase } = useContext(CartContext);

const isInCart = product => {
return !!cartItems.find(item => item.id === product.id);
}

return (
<div className="card card-body">
<img style={{display: "block", margin: "0 auto 10px", maxHeight: "200px"}} className="img-fluid"
src={`${process.env.REACT_APP_HOST}${product.image.url}`} alt=""/>
<p>{product.title}</p>
<h3 className="text-left">{formatNumber(product.price)}</h3>
<div className="text-right">
<Link  to="/" className="btn btn-link btn-sm mr-2">Details</Link>

{
isInCart(product) &&
<button
onClick={() => increase(product)}
className="btn btn-outline-primary btn-sm">Add more</button>
}

{
!isInCart(product) &&
<button type="button"
className="snipcart-add-item btn btn-outline-danger btn-sm"
data-item-name={product.title}
data-item-price={product.price.toFixed(2)}
data-item-description={product.description}
data-item-min-quantity="0"
data-item-max-quantity={product.Qte}
data-item-id={product.id}
data-item-url="/">
Add to cart
</button>
}

</div>
</div>
);
}

export default ProductItem;

As you have seen , the changed lines are fewer than you might do when using other solutions .

  • className="snipcart-add-item” => to add the current item to the shopping cart .
  • data-item-price={product.price.toFixed(2)} => set the price .
  • data-item-description={product.description} => set the description .
  • data-item-min-quantity="0" => set the minimum quantity someone can select while decreasing the number of items (optional) .
  • data-item-max-quantity={product.Qte} => the number of items we have on our store (optional) .
  • data-item-id={product.id} => we must set the id of each product .
  • data-item-url="/" => we must provide the url where the product is displayed .

#Step 6 :

Final step is to test our shopping cart using a test payment form before going to live mode :

If you made it till the end I want to tell you that you have done a great job and if you need any help or advice you can comment below or contact me . Hope you enjoyed this tutorial and find it helpful !

Live demo :

My Store
Web site created using create-react-app

Github repo :

ihssanehatim/ecommerce-reactjs
In this tutorial you will be able to launch your online store in under an hour using the latest technologies : React.js , Strapi and SnipCart . - ihssanehatim/ecommerce-reactjs
Add your comment