In 2018, the total value of online shopping markets was estimated to be around $2.84 trillion. Companies like Amazon, Walmart, and Alibaba top the charts. Through their massive platforms, these giants have opened a new window for small businesses to also operate and own their ecommerce stores.
In this tutorial, we will build an ecommerce website using React for the frontend and 8base for the backend.
N/B: To follow this tutorial, a basic understanding of React and Node.js is required. Please ensure that you have Node and npm/yarn installed before you begin. We’ll also be making use of some GraphQL queries in the project, so some familiarity with GraphQL will be helpful.
React is a component-based JavaScript library for building user interfaces. It allows us to build encapsulated components that manage their state, then compose them to make complex UIs.
8base is a GraphQL backend that lets javascript developers quickly deliver enterprise applications using full-stack JavaScript. It is a front-end framework agnostic, therefore it enables developers to create customer-facing applications however they choose to.
We will use 8base as a backend database layer for our app. This is where we will store the products for our ecommerce website.
8base offers a wide array of features to help developers build performant applications at a faster and much easier rate. Using the 8base Console, you can build your backend using a simple GUI that allows you to do things like:
To get started using 8base, follow the steps listed below:
System Tables
and Your Tables
.Every new 8base workspace automatically comes prepackaged with some built-in tables. These tables are used to handle things like Files, Settings, and Permissions and can all be accessed through the 8base GraphQL API.
Products
, which will consist of the following fields:name: “”
type: Field type is text.
description: “This will be the name of the product”
price: “”
type: Field type is number.
description: “This field will hold the price of our product.”
description: “”
type: Field type is text.
description: “This field will hold the description of our product.”
image: “”
type: Field type is file.
description: “This field will hold the image of our product.”
Settings > Roles > Guest,
and check the appropriate boxes under Products and Files.All unauthenticated users who access your API endpoint are assigned the role of Guest by default.
We won’t cover authentication in this tutorial. You can see how authentication should be handled in more detail here.
In just a few simple steps, we’ve finished setting up a production-ready CMS backend using 8base. Let’s start work on the frontend side of the application.
To start using React, we must first install it. The fastest way to get up and running is by using CRA.
If you don’t already have it installed on your development machine, open your terminal and run the following command:
npx create-react-app
Once the installation is successful, you can now bootstrap a new react project. To create our project run the following command:
npx create-react-app shopping-cart
Start the React app server by running npm start
in a terminal in the root folder of your project.
Let’s start creating the layout for our project. Our app will have 5 different components.
We will make use of bootstrap in our project, so first let’s include it. Open up your index.html
in the public folder and add the following link tag to the head section:
// ./public/index.html <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
Now we can make use of bootstrap classes in our application.
Next, create a components folder and create the following components inside it: Navbar.js, Products.js, Product.js, Footer.js, Cart.js.
Open up the Navbar.js and add the following code:
// src/components/Navbar.js import React from 'react'; const Navbar = () => { return ( <nav className="navbar navbar-light bg-light"> <a className="navbar-brand">Shoppr</a> <button className="btn btn-outline-success my-2 my-sm-0" type="submit">Cart</button> </nav> ); }; export default Navbar;
Open up the Footer.js and add the following code to it:
// src/components/Footer.js import React from 'react'; import '../App.css'; const Footer = () => { return ( <footer className="page-footer font-small bg-blue pt-4"> <div className="container text-center text-md-left"> <div className="row"> <div className="col-md-6 mt-md-0 mt-3"> <h5 className="text-uppercase font-weight-bold">Contact Us</h5> <p>You can contact us on 234-8111-111-11</p> </div> <div className="col-md-6 mb-md-0 mb-3"> <h5 className="text-uppercase font-weight-bold">Return Policy</h5> <p>We accept returns after 7 days max</p> </div> </div> </div> <div className="footer-copyright text-center py-3">© 2019 Copyright: <span> Shoppr</span> </div> </footer> ); }; export default Footer;
Our footer needs some styling so we’d add the following styles to the App.css
file:
// src/App.css footer { position: absolute; bottom: 0; width: 100%; background-color: #333; color:#fff; }
Before we create our products component we need to query 8base to send us our product details to display. Let’s do that now.
To connect our application to the backend we need to install a couple of GraphQL packages. One of the libraries we’d use is apollo-boost, it provides a client for connecting to the GraphQL backend using a URI.
The URI is the endpoint provided by 8base and is available on the data page of the dashboard.
Run the following command in your terminal to install the necessary packages:
npm install apollo-boost graphql graphql-tag react-apollo
Once successful, go ahead and update the index.js
file in the src directory to the following code:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import { ApolloProvider } from "react-apollo"; import ApolloClient from "apollo-boost"; import * as serviceWorker from './serviceWorker'; const client = new ApolloClient({ uri: "<YOUR_8BASE_ENDPOINT>" }); ReactDOM.render( <ApolloProvider client={client}> <App /> </ApolloProvider>, document.getElementById('root') ); serviceWorker.unregister();
We’ve wrapped our entire application with the ApolloProvider
that takes a single prop, the client. The ApolloProvider loads the 8base table schema, which gives you access to all properties of the data model inside your application.
We’ve been able to load our table schema from 8base into our application. The next step is to fetch and display our products.
Create a product-list
folder under the component folder and then create an index.js
file and add the following to it:
// src/components/product-list/index.js import gql from "graphql-tag"; import { graphql } from "react-apollo"; const PRODUCTS_QUERY = gql` query { productsList { items { id createdAt name price description image { downloadUrl } } } } `; export default PRODUCTS_QUERY;
Here, we create a constant called PRODUCTS_QUERY
that stores the query. The gql
function is used to parse the plain string that contains the GraphQL code.
We’ve already populated the backend with some data. To test if our query works properly, 8base provides a handy GraphQL explorer specifically for this. In the menu of your 8base dashboard click on the API explorer icon and run the query.
Now, we are certain our query works as it should. Let’s go ahead and create our product’s components.
Open up your the Products.js
component and add the following code to it:
// src/components/Products.js import React, { Component } from 'react'; import { Query } from 'react-apollo'; import PRODUCTS_QUERY from './product-list/index'; import Product from './Product'; import Navbar from './Navbar'; class Products extends Component { constructor(props) { super(props); this.state = { cartitems: [] }; } addItem = (item) => { this.setState({ cartitems : this.state.cartitems.concat([item]) }); } render() { return ( <Query query={PRODUCTS_QUERY}> {({ loading, error, data }) => { if (loading) return <div>Fetching</div> if (error) return <div>Error</div> const items = data.productsList.items; return ( <div> <Navbar/> <div className="container mt-4"> <div className="row"> {items.map(item => <Product key={item.id} product={item} addItem={this.addItem} />)} </div> </div> </div> ) }} </Query> ); } };
export default Products;Here, we wrap our products with the <Query/>
component and pass the PRODUCTS_QUERY
as props.
Apollo injected several props into the component’s render prop function
. These props themselves provide information about the state of the network request:
loading
: This is true
as long as the request is still ongoing and the response hasn’t been received.error
: In case the request fails, this field will contain information about what exactly went wrong.data
: This is the actual data that was received from the server. It has the items
property which represents a list of product
elements.Finally, we loop through all the received items and pass them as a prop to our Product component. Before we see what it looks like, let’s create our Product
component.
Open up your Product.js
and add the following code to it:
// src/components/Product.js import React from 'react'; const Product = (props) => { return ( <div className="col-sm-4"> <div className="card" style={{width: "18rem"}}> <img src={props.product.image.downloadUrl} className="card-img-top" alt="shirt"/> <div className="card-body"> <h5 className="card-title">{props.product.name}</h5> <h6 className="card-title">$ {props.product.price}</h6> <button className="btn btn-primary" onClick={() => props.addItem(props.product)}>Buy now</button> </div> </div> </div> ); } export default Product;
Our Product.js
is a functional component that receives product details via props and displays them.
We also call the addItem
function on the click method to add the current product to the cart when it is clicked.
Now, all our components are setup we need to import them in our App.js
component which is our base component. Open it up and add the following to it:
// src/App.js import React from 'react'; import './App.css'; import Footer from './components/Footer'; import Products from './components/Products'; function App() { return ( <div className="App"> <Products /> <Footer/> </div> ); } export default App;
Goto, https://localhost:3000 in your browser and you will see the following:
At this point, we have a store that displays products, we need to add functionality to add items to our cart.
To add our cart functionality we’d need to add some more methods to our components.
Update your products.js
to this:
// src/components/products.js import React, { Component } from 'react'; import { Query } from 'react-apollo'; import PRODUCTS_QUERY from './product-list/index'; import Product from './Product'; import Cart from './Cart'; import Navbar from './Navbar'; class Products extends Component { constructor(props) { super(props); this.state = { cartitems: [] }; this.addItem = this.addItem.bind(this); } addItem(item){ this.setState({ cartitems : this.state.cartitems.concat([item]) }); } showModal = () => { this.setState({ show: true }); }; hideModal = () => { this.setState({ show: false }); }; render() { return ( <Query query={PRODUCTS_QUERY}> {({ loading, error, data }) => { if (loading) return <div>Fetching</div> if (error) return <div>Error</div> const items = data.productsList.items; const itemssent = this.state.cartitems; return ( <div> <Navbar cart={itemssent} show={this.showModal} /> <Cart show={this.state.show} items={itemssent} handleClose={this.hideModal}> </Cart> <div className="container mt-4"> <div className="row"> {items.map(item => <Product key={item.id} product={item} addItem={this.addItem} />)} </div> </div> </div> ) }} </Query> ) }; }; export default Products;
Update your Navbar.js
with the following code:
// src/components/Navbar.js import React from 'react'; const Navbar = (props) => { return ( <nav className="navbar navbar-light bg-light"> <h3>Shoppr</h3> <button className="btn btn-outline-success my-2 my-sm-0" onClick={() => props.show()}>Cart {(props.cart.length)}</button> </nav> ); }; export default Navbar;
Now, create a Cart.js
file and add the following code to it:
import React from 'react'; const Cart = ({ handleClose, show, items }) => { return ( <div className={show ? "modal display-block" : "modal display-none"}> <section className="modal-main"> {items.map(item => <div className="card" style={{width: "18rem"}}> <img src={item.image.downloadUrl} className="card-img-top" alt="shirt"/> <div className="card-body"> <h5 className="card-title">{item.name}</h5> <h6 className="card-title">$ {item.price}</h6> </div> </div> )} Total items: {items.length} <button className="btn btn-warning ml-2" onClick={handleClose}>close</button> </section> </div> ); }; export default Cart;
We need a bit of styling to display our cart modal properly. Open up your app.css
and add the following code to it:
.modal { position: fixed; top: 0; left: 0; width:100%; height: 100%; background: rgba(0, 0, 0, 0.6); } .modal-main { position:fixed; background: white; width: 80%; height: auto; top:50%; left:50%; padding: 10px; transform: translate(-50%,-50%); } .display-block { display: block; } .display-none { display: none; }
Now open your shopping cart add items to it and view it via the cart button:
In this tutorial, we have created a basic ecommerce store. The concepts learned here can help you create powerful ecommerce websites without worrying about your backend infrastructure. You can learn more about React here and 8base here. You can find the code used in this tutorial here.
Happy coding.
LogRocket is like a DVR for web and mobile apps and websites, recording literally everything that happens on your ecommerce app. Instead of guessing why users don’t convert, LogRocket proactively surfaces the root cause of issues that are preventing conversion in your funnel, such as JavaScript errors or dead clicks. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Start proactively monitoring your ecommerce apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]