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
1
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")], };
123456789101112131415161718
// postcss.config.js module.exports = { plugins: ["tailwindcss"], };
12345
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; }
1234567891011121314151617181920
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;
123456789101112
Next is run the project
yarn dev
1
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;
12345678910111213141516171819
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> ....
12345678910111213141516
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
1
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;
123456789101112131415161718192021
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> ); }; ...
1234567891011121314151617181920212223242526272829303132
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
Side note: usehooks-ts is a useful library that contains many custom hooks for typescript. be sure to check it out!
#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
12
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> ); }; ...
123456789101112131415161718192021222324252627282930313233
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>
12345
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
Credits: Image by Rahul Yadav from Pixabay