For this tutorial, you will be utilizing React, npm, Node.js, Bootstrap, and Reactstrap. To keep all of the development environments static, I would advise everyone to use the same text editor like myself, Visual Studio Code, for this tutorial. If you are not familiar with React, I would recommend that you try this tutorial here first.

The purpose of this tutorial is to be able to familiarize oneself with React while creating a card game menu gallery.

Prerequisites:

If you don’t have Node.js installed, I would recommend you install it from the source. If you do not have npm, I would recommend installing it for the source as well.

Don't miss out! Offer ends in Access all 200+ courses

Access all 200+ courses New courses added monthly

New courses added monthly Cancel anytime

Cancel anytime Certificates of completion ACCESS NOW

Once you have Node.js setup and npm, you will be able to install the other libraries through the terminal within Visual Studio Code terminal. Before we get into that, let’s have you create a folder and name it CARDUI once you have that built click terminal and open up a new terminal.

Type:

npm init 1 npm init

The npm init command will initialize creating a package.json file.

It will take you through several questions. You can leave everything defaults except for the entry point being index.html. Once you run through everything it will ask you if this is okay, answer yes.

Next run the following command, which will install the create-react-app:

npm install create-react-app 1 npm install create - react - app

What is create-react-app?

Create-react-app lets you scaffold and quickly create React apps with no build configurations.

Run the following to create a new app, naming it cardui:

You will now need to change directories to cardui by typing the following in the terminal:

cd cardui/ 1 cd cardui /

Now we need to install two more libraries. Bootstrap and Reactstrap. Run the following commands in the terminal to install them.

npm install boostrap npm install reactstrap 1 2 npm install boostrap npm install reactstrap

Now Open up the cardui folder and then open up the index.js file in the src folder and then add the following line into the imports:

import 'bootstrap/dist/css/bootstrap.min.css'; 1 import 'bootstrap/dist/css/bootstrap.min.css' ;

This adds a link to the Bootstrap CSS.

Components:

Now besides App.js we will be creating and using three more components in this React app. In addition to these three components, you will be utilizing two non-component JavaScript files that will be used to populate data for the cards and set filters to organize them by type. One of two additional non-component JavaScript files will be in JavaScript object notation, and the other one will be an array. We will export both.

Let’s start by having you create a new folder in the src folder named components and creating three new JavaScript files in there, CardInfo.js, Filter.js, and MainMenu.js. Then make another folder in the src folder named non-components and create two JavaScript filed named cards.js and filters.js. One last folder that you need to create is the images. Create that folder in the public folder. We will be using eight different images for this example supplied from opengameart.org. They are all under the CC0 license and are created by beouliin.

Open up filters.js and type in the following into it:

export const FILTERS = [ 'Fire', 'Grass', 'Water', 'Mountains', 'Air', 'Rock', 'Heart' ]; 1 2 3 4 5 6 7 8 9 export const FILTERS = [ 'Fire' , 'Grass' , 'Water' , 'Mountains' , 'Air' , 'Rock' , 'Heart' ] ;

The const FILTERS will hold the types our cards can be, and this will be the filter attribute we will use to filter our card gallery.

Next, open up cards.js and type in the following into it:

export const CARDS = [ { id: 0, name: "Red Monster", image: "images/id0.jpg", type: "Fire", attack: "5", damage: "5", description: "A monster from the volcanic region." }, { id: 1, name: "Pink Monster", image: "images/id1.jpg", type: "Heart", attack: "4", damage: "2", description: "A monster from the heart region." }, { id: 2, name: "Orange Monster", image: "images/id2.jpg", type: "Mountains", attack: "22", damage: "3", description: "A monster from the heart region." }, { id: 3, name: "Blue Monster", image: "images/id3.jpg", type: "Water", attack: "1", damage: "13", description: "A monster from the water region." }, { id: 4, name: "Green Monster", image: "images/id4.jpg", type: "Grass", attack: "3", damage: "1", description: "A monster from the grass region." }, { id: 5, name: "Winged Monster", image: "images/id5.jpg", type: "Air", attack: "12", damage: "2", description: "A monster from the cloud region." }, { id: 6, name: "Spiky Monster", image: "images/id6.jpg", type: "Rock", attack: "4", damage: "6", description: "A monster from the mountain region." }, { id: 7, name: "Plant Monster", image: "images/id7.jpg", type: "Grass", attack: "8", damage: "4", description: "A monster from the jungle region." } ]; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 export const CARDS = [ { id : 0 , name : "Red Monster" , image : "images/id0.jpg" , type : "Fire" , attack : "5" , damage : "5" , description : "A monster from the volcanic region." } , { id : 1 , name : "Pink Monster" , image : "images/id1.jpg" , type : "Heart" , attack : "4" , damage : "2" , description : "A monster from the heart region." } , { id : 2 , name : "Orange Monster" , image : "images/id2.jpg" , type : "Mountains" , attack : "22" , damage : "3" , description : "A monster from the heart region." } , { id : 3 , name : "Blue Monster" , image : "images/id3.jpg" , type : "Water" , attack : "1" , damage : "13" , description : "A monster from the water region." } , { id : 4 , name : "Green Monster" , image : "images/id4.jpg" , type : "Grass" , attack : "3" , damage : "1" , description : "A monster from the grass region." } , { id : 5 , name : "Winged Monster" , image : "images/id5.jpg" , type : "Air" , attack : "12" , damage : "2" , description : "A monster from the cloud region." } , { id : 6 , name : "Spiky Monster" , image : "images/id6.jpg" , type : "Rock" , attack : "4" , damage : "6" , description : "A monster from the mountain region." } , { id : 7 , name : "Plant Monster" , image : "images/id7.jpg" , type : "Grass" , attack : "8" , damage : "4" , description : "A monster from the jungle region." } ] ;

This will be the detailed information for the eight different types of cards we have. It doesn’t matter what the information in any of the portions is. These, json const variable and list const variable, are just a generic example to show you how to use a json and list within a React application. You can change anything you want and fiddle around with it, but if you make any changes to type you should make those changes to the FILTERS list as well because that is where the check and balances come into play later on.

Let us create an App.css. We will set the background color of the body and set a class type and set its background color as well. Normally you would set a variable within a component and set the properties for those elements within it, but this works just fine as well. It depends on how much you need to style, per component.

body { background-color: lightcyan; } .cardInfo { background-color: lightblue; } 1 2 3 4 5 6 7 body { background - color : lightcyan ; } . cardInfo { background - color : lightblue ; }

Here is an overview of the structure and file structure below. The first image is an outline of how the components relate to each and the second image is the file structure in my personal workplace.

As you can tell from the image below, it is a representation of the parents and child components and how each prop will be passed to its parent component.

Below is a representation of my personal work space. You should all have the same structure of folders and files, up to this point, if you are following along.

Now inside your cardui folder, open up the src folder and open up App.js.

Replace the original Class App Extends Component code with the following:

class App extends Component { render() { return ( <div className="App"> <MainMenu /> </div> ); } } 1 2 3 4 5 6 7 8 9 class App extends Component { render ( ) { return ( < div className = "App" > < MainMenu / > < / div > ) ; } }

Here we are replacing the standard code that comes with creating react app in App.js with our own which will render MainMenu only, as MainMenu will house all the other components.

FILTER.JS COMPONENT:

Now let’s open up the Filter.js file. You will be importing react components, Bootstrap card, and button components and the filter attribute list. Type in the following imports into it:

import React, { Component } from "react"; import { Card, CardImg, ButtonGroup, Button } from "reactstrap"; import { FILTERS } from "../non-components/filters"; 1 2 3 import React , { Component } from "react" ; import { Card , CardImg , ButtonGroup , Button } from "reactstrap" ; import { FILTERS } from "../non-components/filters" ;

The Filter component will render the all the card images and the side navigational filter. You will create two constant variable, card and filterButtons. The const cards will be a property that filters through the MainMenu state called cards for a specific type and maps individual keys to each card at rendering. Keys will be assigned using Math.random().toString(36).slice(2). There is a multitude of issues with generating keys in such a manner, but it will suit us perfectly fine for the scope of this tutorial. You can read more about the complexities of stringifying floating point numbers here.

The class of col-2 and m-1 will create a column of two and a margin of 1.

For the constant variable filterButtons, we will map the attribute types FILTERS from filters.js into a variable and use the fat arrow function to return a button that will have a key which will be the attribute type with an onclick event handler which will run a MainMenu method called cardFilterSelect, which updates the cardFilter state.

We use a combination of Reactstrap and Bootstrap for the quick scaffolding of the front-end. We use the Bootstrap classes for the grid and flexbox class elements and Reactstrap for the button and button groups.

It should look like the following:

class Filter extends Component { render() { const card = this.props.cards .filter(({ type }) => { return !this.props.filter || type === this.props.filter; }) .map(card => { return ( <div key={Math.random() .toString(36) .slice(2)} className="col-2 m-1" > <Card onClick={() => this.props.cardSelect(card.id)}> <CardImg src={card.image} alt={card.name} /> </Card> </div> ); }); const filterButtons = FILTERS.map(filterName => { return ( <Button key={filterName} onClick={() => this.props.cardFilterSelect(filterName)} > {filterName} </Button> ); }); return ( <div className="container"> <div className="row"> <div className="col d-flex align-content-start flex-wrap">{card}</div> <div className="p-5 col-1"> <ButtonGroup size="lg" vertical> <h6 style={{ color: "blue" }}>Card Attributes</h6> <Button onClick={() => this.props.cardFilterSelect(null)}> All </Button> {filterButtons} </ButtonGroup> </div> </div> </div> ); } } export default Filter; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class Filter extends Component { render ( ) { const card = this . props . cards . filter ( ( { type } ) = > { return ! this . props . filter | | type === this . props . filter ; } ) . map ( card = > { return ( < div key = { Math . random ( ) . toString ( 36 ) . slice ( 2 ) } className = "col-2 m-1" > < Card onClick = { ( ) = > this . props . cardSelect ( card . id ) } > < CardImg src = { card . image } alt = { card . name } / > < / Card > < / div > ) ; } ) ; const filterButtons = FILTERS . map ( filterName = > { return ( < Button key = { filterName } onClick = { ( ) = > this . props . cardFilterSelect ( filterName ) } > { filterName } < / Button > ) ; } ) ; return ( < div className = "container" > < div className = "row" > < div className = "col d-flex align-content-start flex-wrap" > { card } < / div > < div className = "p-5 col-1" > < ButtonGroup size = "lg" vertical > < h6 style = { { color : "blue" } } > Card Attributes < / h6 > < Button onClick = { ( ) = > this . props . cardFilterSelect ( null ) } > All < / Button > { filterButtons } < / ButtonGroup > < / div > < / div > < / div > ) ; } } export default Filter ;

CARDINFO.JS COMPONENT:

Next open up CardInfo.js and import React and Reactstrap.

import React, { Component } from "react"; import { Card, CardImg } from "reactstrap"; 1 2 import React , { Component } from "react" ; import { Card , CardImg } from "reactstrap" ;

The class CardInfo will extend Component and we will create two custom methods, then render the class out. The two methods that will be created are renderCard and renderInfo. Both will take a single parameter known as the card. RenderCard will return a card component with a card image and renderInfo will return a list of all the card information. We separate each item into a list item in an unordered list, assign headings and variables. Remember this component, CardInfo, will only render if it not null – which can be triggered by the Filter component.

class CardInfo extends Component { renderCard(card) { return ( <Card> <CardImg src={card.image} alt={card.name} /> </Card> ); } renderInfo(card) { return ( <ul className="list-unstyled"> <li> <h6>Name :</h6> <div>{card.name}</div> </li> <li> <h6>Type : </h6> <div>{card.type}</div> </li> <li> <h6>Attack: </h6> <div>{card.attack}</div> </li> <li> <h6>Damage : </h6> <div>{card.damage}</div> </li> <li> <h6>Description : </h6> <div>{card.description}</div> </li> </ul> ); } render() { const card = this.props.card; if (card != null) return ( <div className=" cardInfo p-5 "> <div className="row"> <div className="col-12 col-md-5 "> <p>hi</p> {this.renderCard(card)} </div> <div className="col-12 col-md-5 border border-primary"> <h4>Card Information</h4> {this.renderInfo(card)} </div> </div> </div> ); else return <div />; } } export default CardInfo; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class CardInfo extends Component { renderCard ( card ) { return ( < Card > < CardImg src = { card . image } alt = { card . name } / > < / Card > ) ; } renderInfo ( card ) { return ( < ul className = "list-unstyled" > < li > < h6 > Name : < / h6 > < div > { card . name } < / div > < / li > < li > < h6 > Type : < / h6 > < div > { card . type } < / div > < / li > < li > < h6 > Attack : < / h6 > < div > { card . attack } < / div > < / li > < li > < h6 > Damage : < / h6 > < div > { card . damage } < / div > < / li > < li > < h6 > Description : < / h6 > < div > { card . description } < / div > < / li > < / ul > ) ; } render ( ) { const card = this . props . card ; if ( card ! = null ) return ( < div className = " cardInfo p-5 " > < div className = "row" > < div className = "col-12 col-md-5 " > < p > hi < / p > { this . renderCard ( card ) } < / div > < div className = "col-12 col-md-5 border border-primary" > < h4 > Card Information < / h4 > { this . renderInfo ( card ) } < / div > < / div > < / div > ) ; else return < div / > ; } } export default CardInfo ;

MainMenu.JS COMPONENT:

Now I want you to go ahead and open up MainMenu.js. Here we will be creating and returning the navigational bar for our gallery as well as the Filter and CardInfo components and create several states that will be able to be lifted into the App.js file.

At the top import the following:

import React, { Component } from "react"; import { Navbar, NavbarBrand } from "reactstrap"; import Filter from "./Filter"; import CardInfo from "./CardInfo"; import { CARDS } from "../non-components/cards"; 1 2 3 4 5 import React , { Component } from "react" ; import { Navbar , NavbarBrand } from "reactstrap" ; import Filter from "./Filter" ; import CardInfo from "./CardInfo" ; import { CARDS } from "../non-components/cards" ;

The first thing that gets called here is the constructor with the component props. We call super and pass props into it. Now we can start initializing the states and their default values within it.

Within the MainMenu class, we will create three states, cards for the CARDS const that we exported from the card.js, selectedCard which will be assigned the default value of null to and cardFilter which we will also assign null to. The states of these variables will change later as the application re-renders. For instance, selectedCard will change from null to the card.id that is selected.

class MainMenu extends Component { constructor(props) { super(props); this.state = { cards: CARDS, selectedCard: null, cardFilter: null }; } 1 2 3 4 5 6 7 8 9 10 class MainMenu extends Component { constructor ( props ) { super ( props ) ; this . state = { cards : CARDS , selectedCard : null , cardFilter : null } ; }

Now we will create two methods named cardSelect which will take cardId and cardFilterSelect which will take filterName. Both of these will use the setState to tell the components that it needs to be re-rendered and have its state updated.

cardSelect(cardId) { this.setState({ selectedCard: cardId }); } cardFilterSelect(filterName) { this.setState({ cardFilter: filterName }); } 1 2 3 4 5 6 7 cardSelect ( cardId ) { this . setState ( { selectedCard : cardId } ) ; } cardFilterSelect ( filterName ) { this . setState ( { cardFilter : filterName } ) ; }

Next, we will render everything and export it out. In the render output we combine all the elements into one div. The Navbar is part of the Reactstrap library and makes up the Title in the Navigational Bar.

The Filer component creates the gallery of cards as well as the side bar navigation. Its cards state represents the current state of the cards json const variable from CARDS. The cardSelect uses the fat arrow function expression to assign the cardId its proper cardId when it re-renders during the setState of that method. The filter variable is assigned the cardFilter state and cardFilterSelect uses the fat arrow function expression to assign it the current filterName being used when it re-renders during the setState of that current method that is being used.

The CardInfo uses the filter() method check if it pass the test of card.id being equal to the selectedCard.

render() { return ( <div> <Navbar dark color="primary"> <div className="container"> <NavbarBrand href="/" className="mx-auto"> {" "} Card Selection Menu{" "} </NavbarBrand> </div> </Navbar> <Filter cards={this.state.cards} cardSelect={cardId => this.cardSelect(cardId)} filter={this.state.cardFilter} cardFilterSelect={filterName => this.cardFilterSelect(filterName)} /> <CardInfo card={ this.state.cards.filter( card => card.id === this.state.selectedCard )[0] } /> </div> ); } } export default MainMenu; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 render ( ) { return ( < div > < Navbar dark color = "primary" > < div className = "container" > < NavbarBrand href = "/" className = "mx-auto" > { " " } Card Selection Menu { " " } < / NavbarBrand > < / div > < / Navbar > < Filter cards = { this . state . cards } cardSelect = { cardId = > this . cardSelect ( cardId ) } filter = { this . state . cardFilter } cardFilterSelect = { filterName = > this . cardFilterSelect ( filterName ) } / > < CardInfo card = { this . state . cards . filter ( card = > card . id === this . state . selectedCard ) [ 0 ] } / > < / div > ) ; } } export default MainMenu ;

For more information about Reactstrap, I personally recommend looking over Bootstrap and Reactstrap. They are well documented libraries, Bootstrap more so than Reactstrap.

The final product should look something similar or along the lines as the following screenshot. You download the source code here.

Thanks for following along with this tutorial. Please feel free to leave your comment below if there are any thoughts that you’d like to share.