Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
In this article, we will make a popup modal in React. This modal will look like any advertisement popping up on the screen, but we will do this when we click a button. We will also add some animations when it pops up and add three ways to close this modal. We can close it by using the close button which will be in the modal, next we will close it by clicking the background and lastly, we can do the same using the escape button.
This will be a very basic project, but it will be very useful at the same time. So let’s just make it step by step.
So first thing first is to set up dependencies and install the required packages. Here we will install the styled-components package, so with this, we can actually style the JSX inside the React components. For simplicity, you can say it allows us to add basic internal CSS, here you can’t add advanced CSS like animations, transitions, etc. You can install it with the below line of command.
npm install styled-components
Next, we will install react-spring
a package, so with this, we can add some animations to our components. Since we need some animations for our modal, then we need this package.
npm install react-spring
Lastly, we need to install react-icons
, as its name suggests that we can add icons using this package, Here we will add a cross icon as our close button.
npm install react-icons
In the App.js file, we are adding the button for modal, As we can see in the below code we have imported all required packages like react, styled-components, we have also added a file named Modal.js, in this file we will add modal. Also, we have added a GlobalStyles.js file, in which we will add some basic styles for global elements.
Then here we can see that we have added some constants like Container, and Button, in which we have added styled.div“. This is the basic syntax when we want to use styled-components for <div>, in this we have applied some basic CSS styles. These constants can be accessed like components, So if we want to use Container, then we can use <Container>. Here is styled.div, div specifies the element, if we want to add a button then we can write styled.button.
So here, we have applied some CSS styling for the elements like <div>, and <button>. Now we want to define the openModal() function, for this, we have added the useState hook, and its value will be false by default. Then in the function, we are just setting the value for the state from false to true. Then we are just adding <Modal> with showModal and setShowModal as props. Lastly, we have just added <GlobalStyle> from the globalStyles file, in which we have just added a little bit of CSS for global elements.
App.js
import React, { useState } from 'react';
import styled from 'styled-components';
import { Modal } from './components/Modal';
import { GlobalStyle } from './globalStyles';
const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
`;
const Button = styled.button`
min-width: 100px;
padding: 16px 32px;
border-radius: 4px;
border: none;
background: #141414;
color: #fff;
font-size: 24px;
cursor: pointer;
`;
function App() {
const [showModal, setShowModal] = useState(false);
const openModal = () => {
setShowModal(showModal => !showModal);
};
return (
<>
<Container>
<Button onClick={openModal}>I'm a modal</Button>
<Modal showModal={showModal} setShowModal={setShowModal} />
<GlobalStyle />
</Container>
</>
);
}
export default App;
Now let’s make the modal, in this, we have added all kinds of packages, Then let’s jump directly to the return(), in this, we have added {showModal} to check where it is true or false. If it is true, then we called <background> in which we have added an onClick listener with closeModal function. In this function, we have we check the condition where modelRef.current === e.target, if it is the same, then we update the hook with false.
Then we added animation on the modal div with <animated.div>, we added some duration with a certain duration, we also checked showModal value if true then we added opacity to 1 and also added translateY to 0. Otherwise, we just add 0 opacity and -100% translateY.
After that, we will add the modal in showModal, in this we added an image here, also we have also added some random text as content. Now on the close button, we have added an onClick listener, on which we have added again showModel value from true to false. We again added some basic styling on these components using styled-components package.
By doing these many things, we are able to see the modal after the button gets clicked, and get closed if the close button gets hit. Now we need to add functionality to close the modal while clicking the background and escape button. For the Escape button, we have added a callback function, in this, we are checking the e.key with escape if it is matched then we update the setShowModal with a false value. For Background, we have added an event listener to listen “keydown”, here we also use the Keypress function again because we already wrote the logic to remove the Modal, so we just need to call this function here as well.
import React, { useRef, useEffect, useCallback } from 'react';
import { useSpring, animated } from 'react-spring';
import styled from 'styled-components';
import { MdClose } from 'react-icons/md';
const Background = styled.div`
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
position: fixed;
display: flex;
justify-content: center;
align-items: center;
`;
const ModalWrapper = styled.div`
width: 800px;
height: 500px;
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.2);
background: #fff;
color: #000;
display: grid;
grid-template-columns: 1fr 1fr;
position: relative;
z-index: 10;
border-radius: 10px;
`;
const ModalImg = styled.img`
width: 100%;
height: 100%;
border-radius: 10px 0 0 10px;
background: #000;
`;
const ModalContent = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1.8;
color: #141414;
p {
margin-bottom: 1rem;
}
button {
padding: 10px 24px;
background: #141414;
color: #fff;
border: none;
}
`;
const CloseModalButton = styled(MdClose)`
cursor: pointer;
position: absolute;
top: 20px;
right: 20px;
width: 32px;
height: 32px;
padding: 0;
z-index: 10;
`;
export const Modal = ({ showModal, setShowModal }) => {
const modalRef = useRef();
const animation = useSpring({
config: {
duration: 250
},
opacity: showModal ? 1 : 0,
transform: showModal ? `translateY(0%)` : `translateY(-100%)`
});
const closeModal = e => {
if (modalRef.current === e.target) {
setShowModal(false);
}
};
const keyPress = useCallback(
e => {
if (e.key === 'Escape' && showModal) {
setShowModal(false);
console.log('I pressed');
}
},
[setShowModal, showModal]
);
useEffect(
() => {
document.addEventListener('keydown', keyPress);
return () => document.removeEventListener('keydown', keyPress);
},
[keyPress]
);
return (
<>
{showModal ? (
<Background onClick={closeModal} ref={modalRef}>
<animated.div style={animation}>
<ModalWrapper showModal={showModal}>
<ModalImg src={require('./modal.jpg')} alt='camera' />
<ModalContent>
<h1>Are you ready?</h1>
<p>Get exclusive access to our next launch.</p>
<button>Join Now</button>
</ModalContent>
<CloseModalButton
aria-label='Close modal'
onClick={() => setShowModal(prev => !prev)}
/>
</ModalWrapper>
</animated.div>
</Background>
) : null}
</>
);
};
App.js
import React, { useState } from 'react';
import styled from 'styled-components';
import { Modal } from './components/Modal';
import { GlobalStyle } from './globalStyles';
const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
`;
const Button = styled.button`
min-width: 100px;
padding: 16px 32px;
border-radius: 4px;
border: none;
background: #141414;
color: #fff;
font-size: 24px;
cursor: pointer;
`;
function App() {
const [showModal, setShowModal] = useState(false);
const openModal = () => {
setShowModal(showModal=>!showModal);
};
return (
<>
<Container>
<Button onClick={openModal}>I'm a modal</Button>
<Modal showModal={showModal} setShowModal={setShowModal} />
<GlobalStyle />
</Container>
</>
);
}
export default App;
Modal.js
import React, { useRef, useEffect, useCallback } from 'react';
import { useSpring, animated } from 'react-spring';
import styled from 'styled-components';
import { MdClose } from 'react-icons/md';
const Background = styled.div`
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
position: fixed;
display: flex;
justify-content: center;
align-items: center;
`;
const ModalWrapper = styled.div`
width: 800px;
height: 500px;
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.2);
background: #fff;
color: #000;
display: grid;
grid-template-columns: 1fr 1fr;
position: relative;
z-index: 10;
border-radius: 10px;
`;
const ModalImg = styled.img`
width: 100%;
height: 100%;
border-radius: 10px 0 0 10px;
background: #000;
`;
const ModalContent = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1.8;
color: #141414;
p {
margin-bottom: 1rem;
}
button {
padding: 10px 24px;
background: #141414;
color: #fff;
border: none;
}
`;
const CloseModalButton = styled(MdClose)`
cursor: pointer;
position: absolute;
top: 20px;
right: 20px;
width: 32px;
height: 32px;
padding: 0;
z-index: 10;
`;
export const Modal = ({ showModal, setShowModal }) => {
const modalRef = useRef();
const animation = useSpring({
config: {
duration: 250
},
opacity: showModal ? 1 : 0,
transform: showModal ? `translateY(0%)` : `translateY(-100%)`
});
const closeModal = e => {
if (modalRef.current === e.target) {
setShowModal(false);
}
};
const keyPress = useCallback(
e => {
if (e.key === 'Escape' && showModal) {
setShowModal(false);
console.log('I pressed');
}
},
[setShowModal, showModal]
);
useEffect(
() => {
document.addEventListener('keydown', keyPress);
return () => document.removeEventListener('keydown', keyPress);
},
[keyPress]
);
return (
<>
{showModal ? (
<Background onClick={closeModal} ref={modalRef}>
<animated.div style={animation}>
<ModalWrapper showModal={showModal}>
<ModalImg src={require('./modal.jpg')} alt='camera' />
<ModalContent>
<h1>Are you ready?</h1>
<p>Get exclusive access to our next launch.</p>
<button>Join Now</button>
</ModalContent>
<CloseModalButton
aria-label='Close modal'
onClick={() => setShowModal(prev => !prev)}
/>
</ModalWrapper>
</animated.div>
</Background>
) : null}
</>
);
};
Check out video reference here:
Hey! thanks so much for the great tutorial 🙂
Can I ask, why exactly are you using useCallback for keypress function?
Just trying to understand..
you can use useEffect instead of useCallback hook, but we have used useCallBack to avoid unnecessary rendering of the application. There will no visible change you will find when you create the same app with useEffect. Here we don’t have very large application, so you can use any of them.