Create an E-commerce Products Page in ReactJS
Tags: react, tutorial, javascript
Published: 2021-06-08 16:50:32

E-commerce has revolutionized every aspect of purchasing goods, and the need for skilled web developers has never been greater. In this post, I will walk you step-by-step through the creation of a custom products page using ReactJS. You can read more about React [here](https://reactjs.org/). 

Fiddle: https://jsfiddle.net/x159wmgj/
Github: https://github.com/benwoodman/React-Product-Page

Let's start by creating our products page. Here's a boilerplate template that we can use.

**products.html**
```html
<html>

<head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css">
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <div id='wrapper'>
        <h1 class='title'>Cars for Sale</h1>
        <div id='product-catalog'></div>
    </div>

    <script src="components.js" type="text/babel"></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js" integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script type="text/babel" src="main.js"></script>
</body>

</html>
```

### In a nutshell
* In the head of the document, we are loading some predefined styles from the Bulma CSS library and a file containing our own custom styles we will use to construct our page.
* In the body, we will put all of our content in a div named "wrapper". Right now the wrapper only contains a static title element and a div that we will fill dynamically with our products. 
* We then load our necessary scripts: Babel, React, ReactDOM, Axios, our custom components file, and a main.js file that we will use to set up our page.

Let's start by designing a static product tile that we can use as the template for the generated product tiles. Create a file named "components.js" in your root directory.

React separates these common "templates" into something called *components*. These components can be reused an infinite number of times to construct elements on the page, making it very easy to generate dynamic web pages. 

We will create a new React component like so:

**components.js**
```jsx
class ProductTile extends React.Component {
    
}
```

React components are predefined classes that we can extend to create our own components. This is called [inheritance](https://stackify.com/oop-concept-inheritance/) in Object Oriented Programming. 

We now can go ahead and start creating our first component, ProductTile. This will be an individual product listing on the product page and will look something like this. 

![ProductTile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7hfth3ky08dusxzhslk2.png)

For the design, we will use a predefined element called a Bulma card. You can read up more about that [here](http://bulma.io/documentation/components/card/).

In our ProductTile class, we will create a function named "render" that React will use to render our component in the [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction). 

**components.js**
```jsx
class ProductTile extends React.Component {
    render() {
        return (
            ...
        )
    }
}
```

Within our return statement, we will add [JSX code](https://reactjs.org/docs/introducing-jsx.html) (which looks very similar to HTML syntax) to render our tile. We can use JSX code in our components file because of these two lines of code in our products.html file. 

**products.html**
```html
<script src="components.js" type="text/babel"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
```

We use the [Babel interpreter](https://babeljs.io/) to compile the JSX code into Javascript code that the browser can understand. 

Let's add the tile JSX information.

**components.js**
```jsx
class ProductTile extends React.Component {
    render() {
        return (
            <div class="card">
                <div class="card-image">
                    <figure class="image is-4by3">
                        <img src='img.jpg' alt="Placeholder image"></img>
                    </figure>
                </div>
                <div class="card-content">
                    <p class="title product-title">MKX</p>

                    <div class="content">
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.
                        <br></br>
                    </div>
                    <a class="button is-primary" href="product.html" target="_blank">
                        <strong>More Details</strong>
                    </a>
                </div>
            </div>
        )
    }
}
```

Let's add some styles... (you can do whatever you want here).

**styles.css**
```css
:root {
    --main-background-color: #F9FBF2;
    --main-top-background-color: #FBFEF9;
    --main-accent-color: #FB8F67;
    --title-color: #1E2019;
}

.button.is-primary {
    background-color: var(--main-accent-color);
}

.button.is-primary:hover {
    background-color: var(--title-color);
}

.title {
    color: var(--main-accent-color);
    font-family: 'Inconsolata', monospace;
    font-size: 3em;
}

#inner-large-banner {
    height: 40%;
    width: 100%;
    background-image: url('img/forest.jpg');
    background-size: cover;
    background-position: 50% 25%;
}

#wrapper {
    width: 80%;
    min-height: 70%;
    /* margin-top: -100px; */
    background-color: var(--main-top-background-color);
    margin: -100px auto 0 auto;
    padding: 30px;
    box-shadow: 0 0.5em 1em -0.125em rgb(10 10 10 / 10%), 0 0 0 1px rgb(10 10 10 / 2%);
    border-radius: .25rem;
}

#product-catalog {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    gap: 20px;
}

.product-title {
    font-size: 2em;
}
```

We have now defined a static React component that we can render on our page. To test it, we will add a single line of code in our main.js file to render it within the "product-catalog" div in our products.html file.

**main.js**
```jsx
ReactDOM.render(<ProductTile></ProductTile>, document.getElementById('product-catalog'));
```

Upon adding this line of code, you should see something like this appear on your page:

![ProductTile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7hfth3ky08dusxzhslk2.png)

Incredible! Now let's set up a ProductCatalog component that will house multiple of these tiles.

**components.js**
```jsx
class ProductCatalog extends React.Component {
    renderTile = () => {
        
    }

    render() {
        
    }
}
```

I have set up two methods within our class: one to render an individual ProductTile and one to render the collection of ProductTiles. Let's start with our method to return an individual ProductTile. 

**components.js**
```jsx
class ProductCatalog extends React.Component {
    renderTile = () => {
        return <ProductTile></ProductTile>;
    }

    render() {
        return this.renderTile();
    }
}
```

We now have a ProductCatalog that returns a single ProductTile, but we need it to render more than one ProductTile. We can achieve this by creating a list, adding multiple ProductTiles to it, and then returning the list. 

**components.js**
```jsx
class ProductCatalog extends React.Component {
    renderTile = () => {
        return <ProductTile></ProductTile>;
    }

    render() {
        let tiles = [];
        for (let i = 0; i < 8; i++) {
            tiles.push(this.renderTile());
        }
        return tiles;
    }
}
```

Let's edit our main.js file to return a ProductCatalog rather than a ProductTile.

**main.js**
```jsx
ReactDOM.render(<ProductCatalog></ProductCatalog>, document.getElementById('product-catalog'));
```

The result:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2t14buhcj8gjaa2kbyc.png)

Our design is now complete! Now you're probably wondering how we customize each ProductTile to represent a separate product in the store. We need to start by fetching our JSON products file I talked about earlier. You can serve this up on a webserver or add it directly into your project. 

**main.js**
```jsx
// Our data:
const products = [
    {
        "id": "1",
        "name": "Touareg",
        "image": "/product_img/Mazda_CX-9_2011_34_20_270_37_6_76_68_200_16_AWD_7_4_SUV_gJa.jpg",
        "cost": "677.32",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "2",
        "name": "DeVille",
        "image": "/product_img/Chevrolet_Camaro_2019_31_18_270_20_4_74_52_188_20_RWD_4_2_Convertible_Xcu.jpg",
        "cost": "3474.41",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "3",
        "name": "F250",
        "image": "/product_img/Jaguar_F-Pace_2017_41_18_180_20_4_76_65_186_26_AWD_5_4_SUV_Thp.jpg",
        "cost": "2878.07",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "4",
        "name": "Corvette",
        "image": "/product_img/Bentley_Flying Spur_2011_181_19_550_60_12_77_55_208_11_AWD_5_4_4dr_FLx.jpg",
        "cost": "4379.01",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "5",
        "name": "MKX",
        "image": "/product_img/Audi_R8_2017_189_19_610_52_10_76_48_174_14_AWD_2_2_2dr_nUS.jpg",
        "cost": "3274.66",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "6",
        "name": "Accord",
        "image": "/product_img/Cadillac_CT6_2016_58_18_260_20_4_74_57_204_22_RWD_5_4_4dr_yfN.jpg",
        "cost": "758.8",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "7",
        "name": "Suburban 2500",
        "image": "/product_img/Chevrolet_Silverado 2500HD_2017_54_20_360_60_8_80_78_nan_nan_RWD_5_4_Pickup_FvP.jpg",
        "cost": "2691.62",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    },
    {
        "id": "8",
        "name": "LR2",
        "image": "/product_img/Porsche_Macan_2015_49_19_340_30_6_76_63_184_17_AWD_5_4_SUV_mjd.jpg",
        "cost": "4801.29",
        "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.",
        "bullet_description": [
            "Capacity Spacious: The 2 person tent designed for a roomy and convenient experience. The camping tent with an interior center height of 47 inches and 82.7 x 82.7 inches base dimensions, it is a spacious camp tent with plenty of space for you",
            "Stable & Safe: To keep your camp equipment dry, the sturdy polyester material on the outdoor tent body and the removable rain fly help to ensure water does not make its way into the inside of your waterproof tent for a dry travel",
            "Ventilation Design: Large window design for enhanced airflow. A D-style door will keep an open view. Also, two person tent comes with a storage pocket that helps to clear clutter and keep the inside of the dome tent organized. Great tents for camping",
            "Easy Set-Up & Carry: The camp tent equip with 2 ropes and 6 stakes for safe and stable. It only takes 1-2 people 5 minutes to set up. The carry bag weighs only 5.7lbs. A lightweight tent can be store in the car without taking up much space"
        ]
    }
]

...
```

We can pass this list of products to the ProductCatalog component by using something called component props. The syntax is very similar to HTML properties. We pass it like so:

**main.js**
```jsx
...

const setup = function() {
    ReactDOM.render(<ProductCatalog products={products}></ProductCatalog>, document.getElementById('product-catalog'));
}

setup();
```

Now, when ProductCatalog is rendered by React, it will have access to the products JSON list. Let's edit our ProductCatalog element to render ProductTiles with this information. 

**components.js**
```jsx
class ProductCatalog extends React.Component {
    renderTile = (current_item) => {
        return <ProductTile product={current_item}></ProductTile>;
    }

    render() {
        let tiles = [];
        for (let i = 0; i < this.props.products.length; i++) {
            const current_item = this.props.products[i];
            tiles.push(this.renderTile(current_item));
        }
        return tiles;
    }
}

...
```

Notice that we can access our list of products by the "this.props.products" property. Notice how we also pass the current product to the renderTile method so that we can pass the individual product's information to the ProductTile component. The last step is to edit our ProductTile component to use the product information passed to it in the last step. 

**components.js**
```jsx
...

class ProductTile extends React.Component {
    render() {
        return (
            <div class="card">
                <div class="card-image">
                    <figure class="image is-4by3">
                        <img src={this.props.product.image} alt="Placeholder image"></img>
                    </figure>
                </div>
                <div class="card-content">
                    <p class="title product-title">{this.props.product.name}</p>

                    <div class="content">
                        {this.props.product.short_description}
                        <br></br>
                    </div>
                    <a class="button is-primary" href={"product.html?id=" + this.props.product.id.toString()} target="_blank">
                        <strong>More Details</strong>
                    </a>
                </div>
            </div>
        )
    }
}
```

We can insert elements from our product into the JSX code by using curly braces surrounding the value. So for example, to print the product name we use: `<p class="title product-title">{this.props.product.name}</p>`

Upon loading our page, we should be greeted with something like the following: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uov1r8wysaoav8t7gldd.png)

Your product catalog is now finished! To recap, you created a ProductTile that houses an individual product and a ProductCatalog that houses a collection of ProductTiles and rendered them on the DOM. Congratulations!