DEV Community

loading...
Cover image for Making a modal with React and styled-components leveraging dev-tools

Making a modal with React and styled-components leveraging dev-tools

tallangroberg profile image Tallan Groberg 惻12 min read

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.

Alt Text

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:

  1. Basic knowledge of javascript

  2. Basic understanding of react

  3. Basic CSS skills.

  4. nodejs installed.

  5. create-react-app installed.

  6. The ability to install npm packages.

  7. 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
Enter fullscreen mode Exit fullscreen mode

cd into the project directory and open it with a text editor. I will be using vscode

cd modal-tutorial
Enter fullscreen mode Exit fullscreen mode

and open...

code .
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Alt Text

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Import npm at the top.

  2. Make a styled-div.

  3. 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';
Enter fullscreen mode Exit fullscreen mode

Below the closing bracket of the App function and above the default export make the styled-div.

const AppStyles = styled.div`

`;
Enter fullscreen mode Exit fullscreen mode

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 (<>

  </>);
Enter fullscreen mode Exit fullscreen mode

Add the AppStyles variable like its a cool stuff inside the tags.

  return (<>
      <AppStyles>

      </AppStyles>
  </>);
Enter fullscreen mode Exit fullscreen mode

I'm going to add a class inside the AppStyles div in the JSX

  <div className="modal">

        </div>
Enter fullscreen mode Exit fullscreen mode

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;
}

`;
Enter fullscreen mode Exit fullscreen mode

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.

Alt Text

Go to the top right corner and click on the element inspector.

Alt Text

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.

Alt Text

If you see elements and styles highlighted you will see the CSS as it is represented on the page.

Alt Text

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.)

Alt Text

Go to the part of the dev-tools that has a long random sequence of letters as a class name for a div.

Alt Text

Now add flex to the CSS sheet represented in the dev-tools.

Alt Text

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.

Alt Text

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;

Alt Text

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;
Enter fullscreen mode Exit fullscreen mode

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.

Alt Text

Alt Text

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.

Alt Text

Alt Text

Make a new file called CloseModal.js inside the component folder.

Alt Text

Alt Text

Make a skeleton for this component.

import React from 'react';

const CloseModal = () => {
    return (
        <div>

        </div>
    );
};

export default CloseModal;
Enter fullscreen mode Exit fullscreen mode

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">
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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

Alt Text

Alt Text

Give it a height and width of 32px

Alt Text

Copy and paste the additions to the css in the AppStyles

.close-button {
    border: 1px solid black;
    width: 32px;
    height: 32px;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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}
            />
Enter fullscreen mode Exit fullscreen mode

Add props to the CloseModal.js

const CloseModal = (props) => {
Enter fullscreen mode Exit fullscreen mode

Destructure the two props we passed to get under the line above and between the return

    const {setShowModal} = props;
Enter fullscreen mode Exit fullscreen mode

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">
Enter fullscreen mode Exit fullscreen mode

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))}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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">
Enter fullscreen mode Exit fullscreen mode

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>
  </>);
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Discussion

pic
Editor guide
Collapse
link2twenty profile image
Andrew Bone

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.

Keep posting and keep being awesome.

Thumbs up

Collapse
tallangroberg profile image
Tallan Groberg Author

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!

Collapse
madza profile image
Madza

Good write-up šŸ‘Œ
I've always wondered who chose the logo for styled-components šŸ˜ƒšŸ˜ƒ