How to Implement a Modal Component with React

How to Implement a Modal Component with React

Introduction

In this article, we'll walk through the process of implementing a modal component with React.

Image description

The above image shows the user interface (UI) of the project we would be working on. The UI displays a couple of profile cards and each profile card represents a person with their name and profile picture, while the modal component displays additional information about each selected profile, including their name, role, skills, and profile picture.

Let's get started!


Prerequisite

Before starting, ensure that Node.js and NPM are installed on your system. You can check if you have these installed by running the following commands in your terminal:

node -v
npm -v

If you do not have these packages installed on your computer, download and install Node.js from the official website.

Also, a basic knowledge of JavaScript and React is needed.

Setting your React project with Vite

Follow the process outlined in this blog post Setting your React project with Vite to create a new React project.

In the new React project, remove the pre-existing code and styles in the index.css, App.jsx, and App.css files.

Step-by-step process on how to implement a modal component with React

STEP 1: Import packages and files

Inside the project directory, navigate to the App.jsx file and import the necessary package and file.

import { useState } from 'react';
import './App.css';

This imports the useState hook from the 'react' package, which allows you to use state in functional components. It also imports the 'App.css' file for styling.

Within the src directory, create a components folder and add the following files along with their corresponding CSS files: card.jsx, modal.jsx, card.css, and modal.css.

 src/
│  components/
     │  Card/
          Card.jsx
          Card.css
     │  Modal/
          Modal.jsx
          Modal.css
│  App.css/
│  App.jsx/
│  index.css/
│  main.jsx/

STEP 2: Create the App component

In this next step, create a functional component named App and initialize a state variable using the useState hook and import the App.css file, which contains CSS styles for the App component. We also import the Card component from the ./components/card/card file.

import { useState } from 'react';
import './App.css';
import Card from './components/card/card';

const App = () => {

 const [profiles, updateProfiles] = useState([

       { id: 1, name: "Sophia Anderson", role: "Frontend Engineer", skills: ["HTML", "CSS", "JavaScript", "React", "Next.js"], profilePic: "https://rb.gy/9jh39"},

       { id: 2, name: "Isabella Sullivan", role: "Senior Java Developer", skills: ["Java", "Spring Boot", "Spring Framework", "Maven"], profilePic: "https://rb.gy/gs5lt"},

       { id: 3, name: "Ava Reynolds", role: "Backend Developer", skills: ["Node.js", "Express", "MongoDB", "Nest.js"], profilePic: "https://rb.gy/rs32e"},

       { id: 4, name: "Olivia Thompson", role: "Blockchain Developer", skills: ["Web3", "JavaScript", "Solidity", "Rust", "Smart contracts"], profilePic: "https://rb.gy/xbjed"},    
  ])

return (
  <div className='App'>
      <div className='profile-card'>
        {profiles.map(profile =>{
                return(< Card 
                  key={profile.id} 
                  id={profile.id} 
                  name={profile.name} 
                  skills={profile.skills} 
                  role={profile.role} 
                  profilePic={profile.profilePic} 
                  />)
         })}
      </div>
  </div>
);
};

Code explanation

We declare a state variable called profiles and updateProfiles to update the state. The profiles state variable contains an array of user/profile objects with various properties such as id, name, role, skills, and profilePic.

We declare a return statement that returns the JSX code that will be rendered to the DOM.

Next, we use the .map() function on the profiles array which iterates over each profile object in the array and returns a <Card/> component for each profile. The properties of the profile object (such as id, name, skills, role, and profilePic) are passed as props to the <Card/> component.

It is important to pass a key prop key={profile.id} to the card component within the profiles.map function. This is because the key prop value will be used as a unique identifier for each profile in the profiles array.

In React, the key prop is not accessible within the component but it is primarily used by React to update the list and maintain the state and order of the component.

STEP 3: Style the App component

Add the CSS styles below to your App.css file to style the App component.

.App {
    text-align: center;
    margin: 10rem 3rem;
  } 

.profile-card{
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    margin-top: 4rem;
}

STEP 4: Create the card component

In this step, we create a functional component named Card and initialize a state variable using the useState hook and import the Card.css file, which contains CSS styles for the Card component.

import './Card.css'
import Modal from '../Modal/Modal';
import { useState } from 'react';

const Card = ({id, name, profilePic, skills, role}) => { 

const [showModal, setShowModal] = useState(false)

const handleClose = () => {setShowModal(false)}

  return (
    <>  
      <div className="card">
       <div className="number">{id}</div> 
       <div>
       <img className="profile-pic" src={profilePic} alt="profile picture" />
       </div>
       <div className="descr">
       <p className="name">{name}</p>
       <button className="learn-more" onClick={() =>setShowModal(true)}>Click  to learn more</button>
        </div>
       </div>

{showModal && <Modal name={name} role={role} skills={skills} profilePic={profilePic} handleClose={handleClose}/>}
    </>
  );
};

export default Card;

Code explanation

Firstly, we declare a new component called card using the arrow function expression. In the previous step, the card component received an object of props as an argument. We use destructuring to extract some properties (id, name, profilePic, skills, and role) from the props object.

const [showModal, setShowModal] = useState(false)

const handleClose = () => {setShowModal(false)}

In the card component, we declare a state variable called showModal and setShowModal to update the state. The initial value of showModal is set to false to hide the modal. We declare a function named handleClose that sets the showModal state to false, which closes the modal when it is called.

We declare the return statement and then we pass each of the prop values into the JSX element so each profile card can be rendered to the DOM.

<button className="learn-more" onClick={() => setShowModal(true)}>Click to learn more</button>

Next, We pass an onClick event to the learn more button in the card component. When the button is clicked, it triggers an onClick event that calls the setShowModal function with true as the argument. This updates the showModal state and shows the modal.

{showModal && <Modal name={name} role={role} skills={skills} profilePic={profilePic} handleClose={handleClose}/>

Finally, we import the modal component from the modal directory and render the modal component in the card component. The modal component is only rendered if showModal is true, and we pass in various props such as name, role, skills, profilePic, and the handleClose function to the modal component. When the modal is rendered, the handleClose function is passed as a prop to handle its close action.

STEP 5: Style the card component

Add the CSS styles below to your Card.css file to style the card component.

.card {
    border: 1px solid rgba(0, 0, 0, 0.175);
    border-radius: 10px;
  }

  .number{
    text-align: right;
    font-size: 1.5rem;
    font-weight: 700;
    padding: 0.8rem;
  }

  .name{
    font-size: 1.8rem;
    font-weight: 600;
  }

  .profile-pic{
    width: 100%;
    height: 300px;
  }

  .descr{
    margin-bottom: 1.5rem;
  }

  .learn-more{
    padding: 0.8rem;
    outline: none;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 1.5rem;
  }

  .learn-more:hover{
    background-color: #6a6d6d;
    color: #ffff;
  }

  @media screen and (max-width: 1400px) {  
    .profile-card{
      display: flex;
      flex-direction: column;
      margin-top: 1rem;
    }

    .card{
      margin-top: 3rem;
    }
  }

STEP 6: Create the modal component

In this next step, we create a functional component named Modal and import the Modal.css file, which contains CSS styles for the Modal component.

import './Modal.css' 

const Modal = ({name, profilePic, handleClose, skills, role}) => {

  return (
    <div className="modal-backdrop">

        <div className="modal">
            <img className="modal-img" src={profilePic} alt="profile pic" />
            <div className="modal-body">
               <div className='modal-name'>{name}</div>
               <div className='modal-role'>{role}</div>
               <div className="skills">
               <ul>
                    {skills.map(skill => <li key={skill}>{skill}</li>)}
               </ul>
               </div>
            </div>
             <button className="close-btn" onClick={()=> handleClose()}>  Close</button>
        </div>

    </div>
  );
};

export default Modal;

Code explanation

We declare a new component called ‘modal’ using the arrow function expression. In the previous step, the card component received an object of props as an argument. Then, we destructure the specific props such as name, profilePic, handleClose, skills, and role from the props object.

We declare the return statement and then we pass each of the prop values into the JSX elements to be rendered.

{skills.map(skill => <li key={skill}>{skill}</li>)}

The skills prop value consists of a list of skills arrays. So we use the map function to iterate over the skills array. It returns a new array of list items <li> with each skill as its content. The key attribute is set to the skill for proper identification of list items.

<button className="close-btn" onClick={()=> handleClose()}>Close</button>

Lastly, we pass an onClick event to the close button in the modal component. When the button is clicked, it triggers the onClick event that calls the handleClose function, which was passed as a prop to handle the closing action of the modal.

STEP 7: Style the modal component

Add the CSS styles below to your Modal.css file to style the modal component.

.modal-backdrop {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.8);
  }

.modal { 
    max-width: 480px;
    height: 400px;
    margin: 150px auto;
    background-color: #ffff;
    border-radius: 10px;
    padding: 0;
  }

.modal-img{
    border-radius: 50%;
    height: 100px;
    width: 100px;
    margin: 4rem 0 2rem;
}

.modal-name {
    font-size: 2rem;
    font-weight: 600;
}

.modal-role{
    font-size: 1.5rem;
    font-weight: 600;
    margin-top: 1.5rem;
    font-style: italic;
}

.skills ul {
    display: flex;
    padding: 0;
    justify-content: center;
    margin-top: 0;
  }

.skills li {
    list-style-type: none;
    margin-right: 10px;
    color: black;
    background-color: #ebebf2;
    padding: 0.5rem 1rem;
    margin: 2rem 0.5rem;
    font-size: 1.3rem;
    border-radius: 5px;
    font-weight: 600;
}

.close-btn{
    background-color: #ebebf2;
    border-radius: 3px;
    cursor: pointer;
    font-size: 1.5rem;
    padding: 1rem 2rem;
    margin-top: 2rem;
    outline: none;
    border: none;
    font-weight: 500;
    text-transform: uppercase;
  }

  .close-btn:hover,
  .close-btn:focus{
    background-color: #9393B2;
    color: #ffff;
  }

  @media screen and (max-width: 480px) { 
    .skills li {
        padding: 0.5rem 0.8rem;
        margin: 2rem 0.4rem;
        font-size: 1rem;
    }
  }

We have successfully implemented a modal component with React and you can find the complete code for this tutorial on GitHub and the live link.