What is a modal? Most of the time we see them on the web as a pop-up window that prompts us to log-in to a site when we try and do something that only users can. Such as when you're on dev.to and you go to like something and you're prompted to login without leaving the page.
By the definition in web design, it is a graphical control element that is subordinate to an application's main window per google search result
What you will learn:
Best practice for setting up a modal quickly with styled-components.
Why this is useful:
You can have users do site essentials without having to redirect which can save you time coding and make your website run faster because you will run into situations where this will allow you to keep state instead of having to reset with a redirect.
So I don't waste your time,
this is the github of the finished version.
This is the website
logging into dev.to when you want to like something for example.
Prerequisites:
Basic knowledge of javascript
Basic understanding of react
Basic CSS skills.
nodejs installed.
create-react-app installed.
The ability to install npm packages.
I will be using bash/unix terminal but you can follow along if you are using PC or Linux.
The first step is to make a new react app in your parent directory from the terminal.
create-react-app modal-tutorial
cd into the project directory and open it with a text editor. I will be using vscode
cd modal-tutorial
and open...
code .
Make sure everything looks like an ordinary react app and run start in the terminal to make sure you have the boilerplate made.
npm start
Tear down the app and so that you have a blank white screen by changing the App.js to look like this:
import React from 'react';
function App() {
return (
<div>
</div>
);
}
export default App;
Now it's time to make the modal.
We will use styled-components for this but it's worth knowing how to do style objects since it appears to be the standard for react native applications. If you have a solid understanding of react I suggest learning react native so that you can take your react skills to the next level.
go to the command line and npm install styled-components
npm i styled-components
This took a long time for me compared to usual and if yours did too, it may have something to do with this.
Now we need to:
Import npm at the top.
Make a styled-div.
Make the styled-div the parent most div.
At the top under import React from 'react'
add the styled-components import
.
import styled from 'styled-components';
Below the closing bracket of the App
function and above the default export
make the styled-div.
const AppStyles = styled.div`
`;
Something to know is that the AppStyles has to have a capital
A or you will crash the app.
making this a template literal is necessary it will not work with single or double-quotes.
To add this to the component, we want to make fragments that are represented by <> code stuff inside cool tags... </>
I like to add these right in front of the parenthesis
of theApp
's return
statement.
return (<>
</>);
Add the AppStyles variable like its a cool stuff inside the tags
.
return (<>
<AppStyles>
</AppStyles>
</>);
I'm going to add a class inside the AppStyles div in the JSX
<div className="modal">
</div>
We want to represent the modal as a box, give them height, width, and border inside the backticks for the styled div like so.
const AppStyles = styled.div`
.modal {
width: 20px;
height: 20px;
border: 1px solid black;
}
`;
We have a parent-child relationship with these elements. Which means we can use flex to position everything in the center.
This is being read like an empty div if you look in the dev-tools.
We are going to use the dev tools first paradigm which means that we will start by making things look right in the dev tools. You can use Safari or Firefox but I will be using Chrome.
Let's go to the web page and press 3 keys at the same time option/alt + command + j. Alternately you can right-click and f open them with the inspector.
Go to the top right corner and click on the element inspector.
Now when the mouse hovers over an element you can see the CSS associated with it.
The goal is to get everything looking good from the dev tools and copy and paste the changes we make in the dev tools to the codebase. It's important to always copy and paste from the dev tools because if you make any changes that cause a rerender you will lose the changes in the dev tools.
You can take the risk and get this extension however I do not recommend it because it doesn't have enough reviews. If anyone wants to try it and leave a comment, I would appreciate it.
Click on the small box in the top right corner and report back to the HTML in the dev tools.
If you see elements and styles highlighted you will see the CSS as it is represented on the page.
We want to get the parent ready to make changes to the child. so add flex to the parent container. (do this in the dev tools first.)
Go to the part of the dev-tools that has a long random sequence of letters as a class name for a div.
Now add flex to the CSS sheet represented in the dev-tools.
We will get everything centered then copy and paste it to our react code base. I will provide the css to add the project.
Next, we will justify-content: center
so that everything lines up horizontally.
Centering vertically requires us to make a minimum view height so that when we do center it we see that it was centered. If you don't add a view height it appears as though nothing happens.
After you add the min-height: 100vh;
you can see the effect when you add align-items: center;
Before you forget, copy and paste the code from the dev-tools to the codebase inside the backticks and above the .modal
attribute.
display: flex;
justify-content: center;
min-height: 100vh;
align-items: center;
When you get a rerender from your react app, and you try and copy and paste, it will.
Being able to add css is cool but using the dev-tools like this is great for being able to adjust css to look absolutely perfect.
Let's adjust the height and width of the modal class to be the size we might see a modal being.
After 20px
is highlighted hold shift + up-arrow on your D-pad to move with the tens column hold command instead of shift to move it by the hundreds column.
Having a definite pixel width might be fine, but having a % will be even better.
Add 80% to the width and 80vh for the height.
Copy and paste inside the .modal
class brackets.
We want to add a box that a user can exit the modal when clicked and return to the page. To show how styled-components will work down the component tree. I will make a new component for the close modal X that will be positioned in the top left corner.
Start by making a new folder called component.
Make a new file called CloseModal.js inside the component folder.
Make a skeleton for this component.
import React from 'react';
const CloseModal = () => {
return (
<div>
</div>
);
};
export default CloseModal;
The div above will be the box that contains the X so we will add two divs and make them into lines
Start by adding the close-button
class name to the newly created div.
<div className="close-button">
Add two-div tags and give them id's I will be naming them up and down based on what way they slope from left to right.
The new div will look like this.
<div className="close-button">
<div id="up"></div>
<div id="down"></div>
</div>
Go back to the Appstyles div and add the three new attributes.
.close-button {
border: 1px solid black;
}
#up {
border: 2px solid black;
}
#down {
border: 2px solid grey;
}
I like to make borders while I start so that I can see what is happening and delete them after the positioning is correct.
When making UI atoms and molecules like this, it is best to make them multiples of 4 so we go back to the dev-tools and experiment with the right look of an X.
We have everything set up enough to go to the dev-tools and template the look.
In the dev-tools go to the side where you can see the HTML
Give it a height and width of 32px
Copy and paste the additions to the css in the AppStyles
.close-button {
border: 1px solid black;
width: 32px;
height: 32px;
}
Now to position within the modal.
You can do this in grid but I want to show how to do this with flex only because I think it's important to know flex if you want to use react native or master web development.
Having another parent-child relationship, and wanting to move the box to the top right corner. We should add display: flex;
again and flex-direction: row-reverse
. See what happens if you copy the <CloseModal />
The .modal
should look like this inside the curly brackets.
.modal {
display: flex;
flex-direction: row-reverse;
width: 80%;
height: 80vh;
border: 1px solid black;
}
Next, let's use the dev-tools to make a cross inside the box.
Add the transform property to #up
and #down
id's.
You may have noticed that in my pictures they are span tags and in my snippets they are divs. This is because you can't use transform
on span tags.
#up {
border: 2px solid black;
transform: rotate(-45deg);
}
#down {
border: 2px solid grey;
transform: rotate(45deg);
To adjust the fine-tuning of positioning and sizing we go back to the dev-tools.
To do this we will add a position: relative;
so that we move in relation to the parent. Then adjust the top:
, right:
, and width:
to go perfectly through the center.
These are the measurements I came up with for #down
.
position: relative;
top: 10px;
right: 6px;
width: 40px;
For the up div, we will do the same thing as for the down div with the dev tools helping us on our way.
These are the three attributes I found.
position: relative;
top: 14px;
right: 6px;
width: 40px;
Now that we have everything line up. Let's change around the edges of the divs and change the grey color to brown.
To both of the up and the down divs, add a border-radius: 25px;
to round the corners and background-color: black;
on the #down
id so that they will both be the same color.
background-color: black;
border-radius: 25px;
We want to start the functionality for the modal to pop up on a click event.
This means that we can utilize a boolean and use state to make a button hide when clicked.
Go the App.js
and import useState at the top.
import React, { useState } from 'react';
Make a piece of state at the bottom with a boolean as a data type. Add this like between the return
statement and the function App()
declaration.
const [showModal, setShowModal] = useState(false)
Since we are only passing this one level down. I will pass this prop directly to CloseModal
but Redux or context is a good option if you are incorporating this into a larger app.
<CloseModal
setShowModal={setShowModal}
/>
Add props to the CloseModal.js
const CloseModal = (props) => {
Destructure the two props we passed to get under the line above and between the return
const {setShowModal} = props;
We want to make this show or hide based on a click event. To do this we will add an onClick
to show the modal when showModal
is true.
Now to add the on click handler to the close-modal
div.
<div
onClick={() => setShowModal()}
className="close-button">
The reason why we added the anonymous function: () =>
before is because if you don't, and you call a function inside an onClick
, which you have to do in order to pass an argument inside a function for a situation like this, to the onClick handler you will get an infinite rerender.
To set up a change state. We will make, setShowModal
to switch to the opposite of whatever it was.
The onClick
will look like this.
onClick={() => setShowModal(prev => (!prev))}
We can reuse this onClick with our button to show the modal.
Make a button underneath the modal
div.
<button onClick={() => setShowModal(prev => (!prev))}>show </button>
To make this show the button or the modal, we want to use a ternary statement to only show one or the other based on the showModal
being true or false.
The App component should look like this including the
import React, { useState } from 'react';
import styled from 'styled-components';
import CloseModal from './component/CloseModal';
function App() {
const [showModal, setShowModal] = useState(false)
return (<>
<AppStyles>
{showModal === true ? "T" : "f"}
<div className="modal">
<CloseModal
setShowModal={() => setShowModal(prev => (!prev))}
/>
</div>
<button onClick={() => setShowModal(prev => (!prev))}>show </button>
</AppStyles>
</>);
}
// AppStyles...
export default App;
The same onClick that we gave to the button in the App.js we can add to the .modal
div in the CloseModal.js.
<div
onClick={() => setShowModal(prev => (!prev))}
className="close-button">
We are going to change the ternary statement to include what we want to include on true (the CloseModal component) and what the user sees on false, the (button to open the modal).
Go back to the App.js and where it says "T"
. Add the modal
div including the <CloseModal />
component. I'm spacing things out so that the code doesn't get messy.
And move the button to where it says "f"
.
Everything inside the return should look like this.
return (<>
<AppStyles>
{showModal === true
?
<div className="modal">
<CloseModal
setShowModal={setShowModal}
/>
</div>
:
<button onClick={() => setShowModal(prev => (!prev))}>Show</button>
}
</AppStyles>
</>);
Now when you click the Show bottom and the close-modal div you will show or close the button.
A modal isn't any good without something for a user to interact with.
We can add a form in the center of the modal.
inside the .modal
div add a form with an input.
<form>
<input />
</form>
This form isn't going to do anything it's just for demonstration purposes.
To position the form, we can add two pieces of css and add them to the form element instead of by class like we did with the other divs.
margin: auto;
will center the div.
display: flex
and flex-direction: column;
will
form {
margin: auto;
display: flex;
flex-direction: column;
}
Let's remove the border on the close-button
div.
This is also where you could add colors and tweak the positioning of things but for sake of brevity I will leave that up to you to tell us how you did it in the comments below.
A there you have it, you now have a basic modal. Please let me know if you would like to know how to set up a modal that doesn't interfere with the dom elements underneath it and I would be happy to make that tutorial.
Here is the code on Github.
Conclusion.
You can save yourself a lot of time writing CSS code if you use the dev-tools to help you. I personally like using Firefox because they have guiding lines that help with positioning. Chrome and Safari do not have as good built-in tooling at the time of this writing.
Top comments (3)
This is an interesting take on the model, I would probably say it's not a modal in the traditional sense as you're swapping out the pages content rather than overlaying it (though I would love to see a follow up to this article with your take on that).
I wrote an article about making a reusable modal component a few months ago that covers making an modal that can animate in and out. I've linked it below if you're interested.
React: Using portals to make a modal popup
Andrew Bone ・ Aug 7 ・ 4 min read
Keep posting and keep being awesome.
I took a quick look at your tutorial. There is a lot to be learned about DOM manipulation from that tutorial series.
You are right that this is not a modal in the true sense and I will be making a follow up to this tutorial that shows how to do this without switching out DOM elements.
This article was getting long so I decided to release as it is.
Thanks for the support Andrew.
I’m exited to see check out your tutorials!
Good write-up 👌
I've always wondered who chose the logo for styled-components 😃😃