Type to generate UI components from text

OR

Browse thousands of MUI, Tailwind, React components that are fully customizable and responsive.

Explore Components

Mastery of React Modal for Superior User Experience

Modal is a fundamental part of user interface (UI) development that aids visualization of vital content. It facilitates displaying essential information, user interaction, and alerts without navigating away from the current page. When properly utilized, modals can grab attention in a way that boosts user engagement. Displaying a modal dialog component ensures the user focuses on the modal content while dimming or blurring the background. In addition, advances in frontend development have made seamless incorporation of modals easier and more performant.

One such advancement includes a modal as a reusable component that overlays the existing contents of a UI in a React app. It is dynamically rendered based on user interaction or application state changes. Modal in React can be created from scratch as a custom component or using existing libraries or packages, offering developers the flexibility and convenience of implementing modal functionality.

Nevertheless, it is crucial to implement a modal that clearly and concisely communicates its contents without interrupting the user experience. Therefore, this article focuses on understanding modal functionality in React, customizing a React modal for efficiency, advanced utilization, and accessibility concerns.

Understanding Modal in React: Overview

A modal is a utility component that provides a foundation for creating various interactive elements such as dialogs, popovers, lightboxes, or other overlays within the application’s window. It displays a UI element that takes visual precedence over the other elements on the screen, ensuring users’ interaction before proceeding to the parent application.

A modal component in React renders due to user interactions that trigger state changes. This statement implies that state changes associated with the modal in the component or global state control the modal’s visibility. Structuring the modal this way ensures it is reusable throughout the application. This reusability is crucial for maintaining flexibility since its behavior is customizable to suit different use cases or requirements.

Let’s explore how to implement a modal with react-modal in the subsequent sub-section. Before diving in, visit Purecode AI, an AI-powered code automation platform built to help developers improve their workflow. It houses a library of UI components you can customize to suit your React project.

Implementing a React Modal

Implementing a modal in React involves managing user interactions to control the modal’s visibility by updating the component or global state. Developers can control when the modal is displayed or hidden by effectively managing this state, ensuring a seamless user experience within the application.

Basic Implementation

Let’s create a simple example to demonstrate all we have been discussing so far:

import React from "react"
import Modal from "react-modal";
import "./styles.css";

function App() {
  const [modalIsOpen, setModalIsOpen] = React.useState(false);

  function openModal() {
    setModalIsOpen(true);
  }

  function closeModal() {
    setModalIsOpen(false);
  }

  return (
    <div className="App">
      <button onClick={openModal}>Open Modal</button>
      <Modal
        isOpen={modalIsOpen}
        onRequestClose={closeModal}
        contentLabel="Example Modal"
      >
        <section className="modal-header">
          <h2>Modal Header</h2>
          <button onClick={closeModal}>Close</button>
        </section>

        <section>Modal Content</section>
      </Modal>
    </div>
  );
}

export default App;
// styles.css

.App {
  font-family: sans-serif;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.modal-header h2 {
  font-size: 1.5rem;
}

In the example above, we created a state variable to manage the visibility of the modal and two functions to control this modal state, as illustrated below:

Comparing React Modal with React Portal

In React, react-modal and createPortal can manage and render a modal. Being a component library that creates modal dialogs in React, react-modal allows us to customize the appearance and behavior of modals, control their visibility, and handle user interactions within the modal.

However, before react-modal came around, developers used the createPortal API to render elements, especially modal dynamically. This React API enables the rendering of elements outside of the normal React tree hierarchy. It ensures a React component is rendered into a different DOM node, typically outside the main document structure. Creating a modal with createPortal enables the maintenance of encapsulation and state management, providing flexibility and improving the performance of a React application. Consider the following example while creating a modal with createPortal:

// Modal.jsx

import React, { useRef, useEffect } from "react";
import { createPortal } from "react-dom";

const Modal = ({
  children,
  show = false,
  title,
  closeModal,
  maxWidth = 700,
}) => {
  const elRef = useRef(null);

  if (!elRef.current) {
    elRef.current = document.createElement("section");
    elRef.current.classList = "modal";
    elRef.current.style.cssText = `
			z-index: 50;
			background-color: rgba(29, 32, 37, 0.6);
		`;
    elRef.current.setAttribute("aria-modal", true);
    elRef.current.setAttribute("role", "modal");
    elRef.current.setAttribute("id", "modal");
  }

  useEffect(() => {
    const modalWrapper = document.getElementById("modal-root");
    modalWrapper.append(elRef.current);

    return () => modalWrapper.removeChild(elRef.current);
  }, []);

  useEffect(() => {
    if (show) {
      let timeoutId = 0;

      timeoutId = setTimeout(() => {
        elRef.current.classList.add("show-modal");
      }, 100);

      return () => clearTimeout(timeoutId);
    }
  }, [show]);

  return createPortal(
    <section
      style={{
        maxWidth: `${maxWidth}px`,
        backgroundColor: "white",
        padding: "1rem",
        width: "100%",
      }}
    >
      <section className="modal-header">
        {title && <h3>{title}</h3>}
        <button
          onClick={closeModal}
          aria-expanded="false"
          aria-controls="modal"
        >
          ✖
        </button>
      </section>
      {children}
    </section>,
    elRef.current
  );
};

export default React.memo(Modal);
// styles.css

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.App {
  font-family: sans-serif;
}

.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100%;
  padding: 1rem;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  visibility: hidden;
  opacity: 0;
}

.show-modal {
  visibility: visible;
  opacity: 1;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.modal-header h2 {
  font-size: 1.5rem;
}
import React from "react";
import Modal from "./Modal";
import "./styles.css";

function App() {
  const [modalIsOpen, setModalIsOpen] = React.useState(false);

  function openModal() {
    setModalIsOpen(true);
  }

  function closeModal() {
    setModalIsOpen(false);
  }

  return (
    <section className="App">
      <button onClick={openModal}>Open Modal</button>
      {modalIsOpen && (
        <Modal show={modalIsOpen} closeModal={closeModal} title="Example Modal">
          <section>Modal Content</section>
        </Modal>
      )}
    </section>
  );
}

export default App;
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="theme-color" content="#000000" />
    
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    
    <title>React App</title>
  </head>

  <body>
    <noscript> You need to enable JavaScript to run this app. </noscript>
    <div id="root"></div>
    <div id="modal-root"></div>
    <!--
      This HTML file is a template.
    -->
  </body>
</html>

The above example used createPortal to implement a reusable and customizable modal. We guarantee that the modal element is appended to the specified DOM node by including a container element within the public HTML file. This is demonstrated below:

To learn more about building custom modals with the React portal check out the tutorial video below:

Differences between Modal and Dialog in React

The terms modal and dialog are used interchangeably to refer to UI elements that appear on top of on-screen content. In React, a modal and dialog component serve similar purposes that ensure a user interacts with the contents of the overlay. Although they have similar behavior and appearance, they differ in several ways. The table below shows some of these differences between modal and dialog:

ModalDialog
It is a type of dialog that prevents the user from interacting with the rest of the application while open.It can appear as modal dialogs (blocking interaction) or non-modal dialogs (allowing interaction with other content while open).
It demands immediate interaction with its contents.It does not necessarily require immediate interaction.
Modal is a type of window that displays vital information, form inputs, or prompts.Dialog is a type of window that allows the user to perform a specific action or make a choice.

Customizing React Modal Component

The react-modal provides several props to customize various aspects such as styling, behavior, and content. These customization options include the following:

  • Inline styling

  • Classes

  • Transition effects

Utilizing Inline Styling

We can customize the appearance of the modal with custom styles using the react-modal style props, thereby overriding the default styles where necessary:

import React from "react";
import Modal from "react-modal";

function App() {
  const [modalIsOpen, setModalIsOpen] = React.useState(false);

  function openModal() {
    setModalIsOpen(true);
  }

  function closeModal() {
    setModalIsOpen(false);
  }

  return (
    <div>
      <button onClick={openModal}>Open Modal</button>
      <Modal
        isOpen={modalIsOpen}
        onRequestClose={closeModal}
        style={{
          overlay: {
            backgroundColor: "rgba(0, 0, 0, 0.75)",
          },
          content: {
            color: "#2123ba80",
            padding: "1rem",
          },
        }}
        contentLabel="Example Modal"
      >
        <section
          style={{
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <h2>Modal Title</h2>
          <button onClick={closeModal}>Close</button>
        </section>
        <section>Modal Content</section>
      </Modal>
    </div>
  );
}

export default App;
A modal customized with the style props.

Employing Classes

Aside from using the style props, it is often preferable to use CSS classes to configure the react-modal. It provides the className and overlayClassName props to customize the appearance of the modal content and overlay. These props accept a single string as a value containing the class name to apply to the component. Below is an example using CSS classes:

import { useState } from "react";
import Modal from "react-modal";
import "./styles.css";

const App = () => {
  const [isOpen, setIsOpen] = useState(false);

  const openModal = () => setIsOpen(true);
  const closeModal = () => setIsOpen(false);

  return (
    <section>
      <button onClick={openModal}>Open Modal</button>
      <Modal
        isOpen={isOpen}
        onRequestClose={closeModal}
        className="custom-modal"
        overlayClassName="custom-overlay"
        contentLabel="Example Modal"
      >
        <section
          style={{
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            marginBottom: "1rem",
          }}
        >
          <h2>Custom Modal</h2>
          <button onClick={closeModal}>Close Modal</button>
        </section>
        <p>This is a custom modal content.</p>
      </Modal>
    </section>
  );
}

export default App;
// styles.css

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.custom-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.75);
  padding: 0.625rem;
  opacity: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}

.custom-modal {
  background-color: white;
  max-width: 700px;
  width: 100%;
  color: rgb(6, 6, 41);
  border: 2px solid #ccc;
  border-radius: 8px;
  padding: 1rem;
}
A modal customized with classes.

Styling for Transition Effects

The react-modal makes it possible to transition the modal between the open and closed states using classes. Depending on the transition values, the modal can fade in and out, zoom in and out, etc. For the transition effect to work, we need to specify a transition time required for the animation by providing closeTimeoutMS props, expressed in milliseconds. The closeTimeoutMS value has to be similar to the transition timing value in CSS. Consider the following code example:

import { useState } from "react";
import Modal from "react-modal";
import "./styles.css";

function App() {
  const [isOpen, setIsOpen] = useState(false);

  const openModal = () => setIsOpen(true);
  const closeModal = () => setIsOpen(false);

  return (
    <div>
      <button onClick={openModal}>Open Modal</button>
      <Modal
        isOpen={isOpen}
        onRequestClose={closeModal}
        className="Custom-Modal"
        overlayClassName="Custom__Overlay ReactModal__Overlay"
        contentLabel="Example Modal"
        closeTimeoutMS={1000}
      >
        <div
          style={{
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            marginBottom: "1rem",
          }}
        >
          <h2>Custom Modal</h2>
          <button style={{color: "rgb(6, 6, 41)"}} onClick={closeModal}>Close Modal</button>
        </div>
        <p>This is a custom modal content.</p>
      </Modal>
    </div>
  );
}

export default App;
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.Custom__Overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.75);
  padding: 0.625rem;
  opacity: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}

.ReactModal__Overlay {
  opacity: 0;
  transition: opacity 1000ms ease;
}

.ReactModal__Overlay--after-open {
  opacity: 1;
}

.ReactModal__Overlay--before-close {
  opacity: 0;
}

.Custom-Modal {
  background-color: white;
  max-width: 700px;
  width: 100%;
  color: rgb(6, 6, 41);
  border: 2px solid #ccc;
  border-radius: 8px;
  padding: 1rem;
}

In the example above, the classes with the Block Element Modifier (BEM) naming convention were used to apply a fade transition to the modal. The result is illustrated below:

Advanced Utilization of React Modal

Advanced usage of the react-modal involves leveraging its various features and functionalities to create sophisticated and dynamic modal experiences within your React application.

Integration with Redux for State Management

In previous examples, controlling the modal visibility was through the component state. Sometimes, a project might require several disconnected components to handle the modal visibility. Redux comes to the rescue by making the state globally available to the React component tree. By centralizing the modal visibility state in the Redux store, any component in the application can dispatch actions to toggle the visibility of the modal, ensuring consistent behavior across the entire component tree. This approach is beneficial in larger applications where state management becomes more complex, and having a centralized store like Redux can simplify state management tasks. Below is a simple example:

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import Modal from "react-modal";
import { configureStore, createSlice } from "@reduxjs/toolkit";
import { useSelector, useDispatch, Provider } from "react-redux";

//reducer slice
const modalSlice = createSlice({
  name: "counter",
  initialState: { value: false },
  reducers: {
    show: (state) => {
      state.value = true;
    },
    hide: (state) => {
      state.value = false;
    },
  },
});

const { show, hide } = modalSlice.actions;

const modalReducer = modalSlice.reducer;

var store = configureStore({ reducer: { modal: modalReducer } });

function Result() {
  const isOpen = useSelector((state) => state.modal.value);
  const dispatch = useDispatch();

  const openModal = () => {
    dispatch(show());
  };

  const closeModal = () => {
    dispatch(hide());
  };

  return (
    <div>
      <button onClick={openModal}>Open Modal</button>
      <Modal
        isOpen={isOpen}
        onRequestClose={closeModal}
        contentLabel="Redux Managed Modal"
      >
        <div
          style={{
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            marginBottom: "1rem",
          }}
        >
          <h2>Custom Modal</h2>
          <button style={{color: "rgb(6, 6, 41)"}} onClick={closeModal}>Close Modal</button>
        </div>
        <p>This is a custom modal content.</p>
      </Modal>
    </div>
  );
}

function App() {
  return <Result />;
}

const element = (
  <StrictMode>
    <div>
      <Provider store={store}>
        <App />
      </Provider>
    </div>
  </StrictMode>
);

const rootElement = document.getElementById("root");
createRoot(rootElement).render(element);
Modal visibility controlled through Redux state.

Complex Use Cases

Modal is a versatile tool that can add engaging features to an application. It is useful when shifting the user’s attention to a vital part of the UI flow or essential content. React modal simplifies the integration of a modal component and its customization for a variety of complex use cases, including:

  • Form submission

  • Confirmation action

  • User feedback/notification

Form Submission

The react-modal can be used to present a modal window that contains form elements for user inputs. Examples include login, sign up, making purchases, saving form progress, etc. Using a modal for such submission ensures the user gets a success/error message without navigating from the current interface.

Confirmation Action

The react-modal can be customized to show a modal dialog that allows users to confirm an action before proceeding. This confirmation modal may contain options to confirm or cancel an event. Examples include deleting an item, follow-up email, etc.

User Feedback/Notification

The react-modal simplifies the process of showing notifications or feedback messages to users, such as alerts, warnings, or informational messages.

Best Practices

Effective utilization of the react-modal involves following best practices to guarantee seamless user experience and maintainable codebase. These practices include the following:

  • State management: It is vital to manage the visibility of the modal appropriately. Use component state for controlling modal visibility within a single component. However, when dealing with disconnected or multiple components, use a state management tool like Redux to centralize the modal state.

  • Customization: The react-modal ensures the appearance and behavior of the modal are customizable to suit the project requirements. Leverage the available props to achieve the desired style and transition effects.

  • Accessibility: Ensure your modal is accessible to provide equal access and opportunities to users of diverse abilities. Utilize appropriate semantic HTML elements and ARIA attributes to enhance accessibility and user experience.

Accessibility Considerations of React Modal

Since a modal renders on top of the rest of a page’s content, it exists outside the normal DOM element flow, making it difficult for users to interact with content outside an active modal. Therefore, it’s vital to consider accessibility while building a modal dialog component. Nevertheless, react-modal provides a fully accessible modal component, using the WAI-ARIA guidelines to support users of assistive technologies. Below are some of react-modal’s accessibility-oriented features, along with their configuration options:

  • Hide the window content with the App element

  • Ensure keyboard navigation

  • Apply ARIA attributes

Hide the Window Content with the App Element

It is crucial to conceal the other screen content for the benefit of users of screenreaders while the modal is open. We can achieve this with react-modal by providing the Modal.setAppElement with the query selector identifying the root of your app. By calling the setAppElement method before any modal opens, hiding other screen contents is guaranteed. Consider the following code syntax:

Modal.setAppElement('#root');
Modal.setAppElement(document.getElementById('root'));

Furthermore, using a selector that matches multiple elements or passing multiple selectors will hide all of the elements.

Ensure Keyboard Navigation

When the modal is open, keyboard navigation is restricted by default using the tab key. This approach ensures other elements outside the modal do not receive focus unexpectedly. Moreover, when the modal is closed, the focus is restored to the previous element before opening the modal. Passing the shouldReturnFocusAfterClose={false} prop to your modal disables this behavior.

In addition, the modal can be closed with an escape key. Deactivating this behavior involves passing the shouldCloseOnEsc={false} prop. However, disabling this behavior is not recommended because it may cause accessibility issues for keyboard users.

Apply ARIA Attributes

Besides the aria-hidden attribute, other ARIA attributes can make your application more accessible. The ARIA specification provides a complete list of these ARIA attributes.

The react-modal provides the contentLabel props, a dedicated prop that labels the modal content via aria-label if there is no visible label on the screen. If there is already a visible label, the aria-labelledby attribute allows us to point to the specific element with the label using the aria props. This prop accepts an object whose keys are the ARIA attributes you want to set (without the leading aria- prefix). Below is an example that shows how to make a modal title as well as its description accessible:

<Modal
  isOpen={modalIsOpen}
  aria={{
    labelledby: "heading",
    describedby: "full_description"
  }}>
  <h1 id="heading">Alert</h1>
  <div id="full_description">
    <p>Description goes here.</p>
  </div>
</Modal>

Final Thoughts

Modals have become a vital part of the user experience. The versatility of modal in React ensures customization for various use cases. While several methods are available for creating a modal, react-modal stands out by prioritizing accessibility and providing a functional, capable modal component for general use. Mastering its features and functionalities empowers you to create interactive and engaging user interfaces without compromising the user experience.

In addition, visit Purecode AI to explore our cutting-edge code automation platform designed to enhance developer productivity. With its AI-powered capabilities, Purecode AI offers a comprehensive library of customizable UI components tailored for React projects.

Recommended Reading

If you enjoyed this article, consider reading other articles from our blog to elevate your React knowledge:

Ofili Chukwuemeka Timothy

Ofili Chukwuemeka Timothy