March 14th, 2021
This article assumes a basic familiarity with React, hooks and the concept of state.
This is a two part tutorial to create a reusable, accessible React modal component.
Part 1: This article, focuses on creating a Portal to house our Modal, and making it functional and reusable with props.
Part 2: Focuses on adding appropriate ARIA and controlling focus between the modal and the main page to make our component accessible (awesome).
It also assumes you probably read my previous article on Modals or are already familiar with the idea. If not, Read about Modals
Portals let us break out of our React application root div.
A React App contains components nested within each other, rendered into the root DOM element. Each nested component inherits aspects of its containing or parent element. For example, if our App has a width of 800px, and we render a Dashboard component in it, we can expect that our Dashboard will stay within the 800px constraint. In particular, if we add a z-index style or set overflow:hidden
on a parent, our modal as a child element will not display correctly.
In order to get around the inheritance issue, we use portals, which allow us to render our modal in a div outside the root element.
I created a simple react app with create-react-app in the terminal.
npx create-react-app react-modal
In the public folder, open index.html and add a div with the id="modal" to contain our modal portal.
Reusable - fully customizable and can be used in other React projects
Accessible - follows best practices for accessibility including tab focus, esc key response, and aria usage
This article is going to focus on the first point - creating a reusable component. In the next article, I will outline how to implement accessibility. I decided to split it because the article was becoming too long, and I wanted to spend a little extra time making sure I got the accessibility correct.
The modal will look like this:
It will have a title, description, and actions.
Here is some starting code to make our modal meaningful. This takes a list of events and displays each in the DOM with a little button to delete. By the end of this article, when we click on the button to delete an event, a modal will appear, confirm we want to delete the event, and remove that card from the DOM.
In the src folder, create 3 files: events.js DisplayEvents.js EventCard.js
List of Events
DisplayEvents - import the events from above and map them into the EventCard component (below). I also set up a useState hook and an updateEvents method to handle re-rendering the list when an event is deleted.
Here's the Card component, which simply takes the information from the list and displays the information for each event, and a delete button. We'e receiving the updateEvents method in the props, which we will use in our modal.
Edit App.css and App.js
A little CSS to get us off the ground
Edit our App to render DisplayEvents
Okay, that's maybe a lot of starting code, but I wanted to make our modal meaningful. So, thanks for hanging in with me this far. Now, let's dive in!
Here we go! In our src folder, create a new file: Modal.js
Import React and ReactDom (provides specific methods to efficiently manage DOM elements, and where createPortal lives). We make a div for the dark overlay which will cover the page, and a div for the dialog window. We then inject it into the <div id="modal">
that we made in the public/index.html file.
Great! Now how do we open it?
Let's go back to our EventCard Component. When we click the delete button we want the modal to appear.
Import { useState } from react, and create the open=false state. Toggling this value true will show the modal, false will hide it. In our return statement, add an onClick handler to our delete button which sets the state to true when clicked.
When open is true, display the modal by adding a conditional statement under the button.
Go ahead and try it out! It should look like this:
Next:
add some content with props
handle deleting an item
close that modal
We'll create space in our modal for a title, description and some actions to be displayed.
Back in our Event Card we'll create those props. The title and description are strings. The actions will be two buttons, one to confirm deleting the event and one to cancel.
Now the modal should look a little more interesting, but it's not really working yet.
Guess what we need? onClick event handlers for our modal action buttons! Let's get to it!
Clicking on the Delete button on the main page toggles the open state true and displays the modal. Toggling it false hides the modal. So, let's implement this on our modal cancel button.
Did you get it opening and closing?
Now, for the modal delete button. It should delete the item and close the modal.
Alright! That's the basics I wanted to cover in this article:
Create a modal component which accepts props to customize its content and behavior.
This article has gotten a bit lengthy, so I want to take a break here.
improve accessibility with tab focus and arias
add a simple fade in - fade out animation to make transitions smooth
close the modal when the ESC key is pressed, or the overlay is clicked
Thanks for sticking with me to the end of this one! See you next time!
Here's the final code to this point.