How to Implement a Reusable Responsive Modal in React with DaisyUI
Jasser Mark Arioste
There are many ways to create modals in react. In this tutorial, we’re gonna show you how to create a responsive modal using DaisyUI that works for both mobile and desktop. We’ll make a reusable modal, it can have different content can be different but the layout stays the same. Below is an example of the final output.
What makes a good modal?
A responsive modal is a good modal. For example in mobile, if you have an login or one-time password (otp) form, the modal should be at the bottom of the screen. But for desktop it should be at the center. The fields should be automatically focused.
Why use daisyUI for styling?
I really like Daisyui coupled with Tailwindcss because of its versatility or flexibility. You can use it in any project, even if you have MaterialUI.
It has a class modal-bottom and modal-center which just makes it easy for our use case.
Okay, with that out of the way, lets get started!
Project Setup
Let’s setup daisyui + tailwindcss in our project. I’m using a next.js project with typescript. If you’re using a different setup, check how to install daisyui here
yarn add tailwindcss autoprefixer postcss daisyui
Add tailwindcss.config.js (make sure to include daisyui in the plugins) and postcss.config.js in the root directory of our project.
// tailwindcss.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
container: {
center: true,
padding: {
DEFAULT: "1rem",
},
},
},
plugins: [require("daisyui")],
};
// postcss.config.js
module.exports = {
plugins: ["tailwindcss"],
};
Add tailwind classes in our globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
Let’s do a test run if we installed it properly. First we test some classes in index.tsx
import type { NextPage } from "next";
const Home: NextPage = () => {
return (
<div className="container">
<h1 className="text-2xl">Hello world</h1>
<button className="btn btn-primary">Hello</button>
</div>
);
};
export default Home;
Next is run the project
yarn dev
Current output:
Creating the modal
Now that we’re done installing the dependencies, lets create the responsive modal component.
Inside components folder, let’s create a file Modal.tsx
//Modal.tsx
import React from "react";
type Props = {
children: React.ReactNode;
};
const Modal = ({ children }: Props) => {
return (
// we add modal-bottom and modal-middle classes to make it responsive
//add modal-open for now to test the modal
<div className="modal modal-bottom sm:modal-middle modal-open">
{/* we want any content for this modal layout so we just pass the children */}
<div className="modal-box">{children}</div>
</div>
);
};
export default Modal;
Let’s use it in index.tsx
//index.tsx
import Modal from "components/Modal";
...
<Modal>
<h3 className="font-bold text-lg">
Congratulations random Internet user!
</h3>
<p className="py-4">
You havve been selected for a chance to get one year of subscription
to use Wikipedia for free!
</p>
<div className="modal-action">
<label className="btn btn-primary">Yay!</label>
</div>
</Modal>
....
Let’s see how it looks in action in both desktop and mobile:
Modal for desktop
Modal for mobile
Awesome!, Looks pretty good already! It even has the backdrop.
Implementing the open/close state
There are many ways in DaisyUI to open/close a modal just by using pure html. However, in this tutorial we’re using a react approach where we control the state of the modal and use toggle classes to hide/show the modal. First lets install classnames package to easily organize the classes
yarn add classnames
Let’s modify our Modal.tsx component to add an open state and modify the className to that it makes use of classnames :
//Modal.tsx
import React from "react";
import cn from "classnames";
type Props = {
children: React.ReactNode;
open: boolean;
};
const Modal = ({ children, open }: Props) => {
const modalClass = cn({
"modal modal-bottom sm:modal-middle": true,
"modal-open": open,
});
return (
<div className={modalClass}>
<div className="modal-box">{children}</div>
</div>
);
};
export default Modal;
Let’s implement open/close functionality by pressing a button in index.tsx
//index.tsx
...
const Home: NextPage = () => {
//add state and a toggle handler
const [open, setOpen] = useState(false);
const handleToggle = () => setOpen((prev) => !prev);
return (
<div className="container">
<h1 className="text-2xl">Hello world</h1>
{/* opens the modal */}
<button className="btn btn-primary" onClick={handleToggle}>
Hello
</button>
<Modal open={open}>
<h3 className="font-bold text-lg">
Congratulations random Internet user!
</h3>
<p className="py-4">
You havve been selected for a chance to get one year of subscription
to use Wikipedia for free!
</p>
<div className="modal-action">
{/* closes the modal */}
<button className="btn btn-primary" onClick={handleToggle}>
Yay!
</button>
</div>
</Modal>
</div>
);
};
...
Let’s see it in action!
Implementing click-outside functionality
Currently our modal only closes by pressing the “Yay!” button. Let’s add a close functionality when user clicks outside the modal. We do this by adding a disableClickOutside props setting. For this setting we enable click-outside by default and add disableClickOutside when we want to disable it. We’ll also be using useOnClickOutside hook from usehooks-ts package.
First lets install usehooks-ts
#I'm using 2.6.0 since 2.7.1 has a breaking change and needs to upgrade node.js
yarn add usehooks-ts@2.6.0
Let’s modify our Modal.tsx component and implement click-outside functionality:
...
import { useOnClickOutside } from "usehooks-ts";
type Props = {
children: React.ReactNode;
open: boolean;
// add disableClickOutside
disableClickOutside?: boolean;
//add onClose event so that we can close the modal from inside the component
onClose(): void;
};
...
const Modal = ({ children, open, disableClickOutside, onClose }: Props) => {
const ref = useRef(null);
useOnClickOutside(ref, () => {
if (!disableClickOutside) {
onClose();
}
});
const modalClass = cn({
"modal modal-bottom sm:modal-middle": true,
"modal-open": open,
});
return (
<div className={modalClass}>
<div className="modal-box" ref={ref}>
{children}
</div>
</div>
);
};
...
When calling the Modal component in index.tsx , we can modify it if we want to enable or disable clickoutside
//click outside enabled
<Modal open={open} onClose={handleToggle}>
//click outside disabled
<Modal open={open} onClose={handleToggle} disableClickOutside>
Let’s see it in action (Modal with click outside enabled)
Conclusion
We’ve successfully created a working responsive modal in react by using DaisyUI. The modal can handle any content and we’re able to successfully toggle it inside a parent component.
If you like more tutorials like this on react and daisyUI, be sure to sign up to our newseletter!
Resources:
DaisyUI Docs: https://daisyui.com/components/modal/
Full code: https://github.com/jmarioste/daisyui-modal
Demo: https://daisyui-modal.vercel.app/
Credits: Image by Rahul Yadav from Pixabay


