Tailwind CSS in React - Creating Modal Popup
Tags: tailwindcss, react, modal
Published: 2023-11-11 12:31:42

In this post, I will create a Modal Popup component in React with the help of Tailwind CSS. My previous post has the details of how to configure tailwind CSS in a React application which is created using Vite CLI.

{% embed https://dev.to/pramodkumar115/setting-up-tailwind-css-in-react-with-vite-5e6l %}

I have created a video in my Youtube channel on this same topic of creating Modal Popup. Please check the below link.

{% embed https://youtu.be/U7devKdgZts %}

**What is Modal Popup?**
In simple terms, it is a window which appears on top of the application window. User should interact with this window and restrict interaction with underlying content.

**Steps I followed to get this element**
1. Create a React Component "Modal" with two divs one inside another.

2. Outer div acts as container and inner one acts as content.

3. As per the definition, this window should be on top of the current window, I can add these tailwind classes to the container div.
```flex justify-center items-center fixed top-0 w-screen h-screen bg-gray-500 bg-opacity-50```
fixed - position as fixed, the container should be fixed irrespective any scrollers.
top-0 - the container to start from top left corner.
w-screen - full viewport width
h-screen - full viewport height
bg-opacity-50 - opacity 50% on the background to have the underlying main content visible.
flex - Display as flex to have the container as flex layout.
justify-center - This class moves the child div to the center horizontally as the flex direction is row by default.
items-center - this class moves the child div to the center vertically.

4. The child div can have these classes.
```min-h-[400px] ${getWidth} bg-white```

min-h-[400px] - this is to set some minimum height for the content window.
${getWidth} - Caller component can send a prop to decide the width of the modal content. I will explain in detail later.
bg-white - background of the content area.

5. Modal window contains three sections, Header, Content and Footer.

6. I will use the react pattern of Compound Component to have these 3 sections. i.e, - Header, Content and Footer are accessible only through Modal component. Let me explain.

**Header's** classes can be 
p-2 - to have the Header text with a padding.
text-[20px] - As it is the header, let the size of the font be big.
font-bold - to have the text in bold
border-b border-b-gray-500 - Have only border on bottom.
```
const Header = ({children}: {children: ReactNode | any}) => {
    return <div className={"p-2 text-[20px] font-bold border-b border-b-gray-500"}>
        {children}
</div>
}
```

**Content's** classes can be
p-2 - to have Content text with a padding.
min-h-[300px] - minimum height of 300px. Can be controlled by the height of the children.
overflow-auto - to have scroller in case the content is big.
```
const Content = ({children}: {children: ReactNode | any}) => {
    return <div className={"p-2 min-h-[300px] overflow-auto"}>
        {children}
</div>
}
```

Footer can be similar to Header without text being big and bold.
```
const Footer = ({children}: {children: ReactNode | any}) => {
    return <div className={"p-2 border-t border-t-gray-500"}>
    {children}
</div>
}
```

Attach these three components to Modal and hence they will be accessible through Modal.
```
Modal.Header = Header;
Modal.Content = Content;
Modal.Footer = Footer;
```

**Properties to the Modal Component**

show - a boolean property which on an action (button click) on the parent can set this to true/false. With this prop, Modal component can be shown or hidden.
children - The content on the Modal (Header, Content, Footer)
size - this property can determine the size of the modal content div. This can text values like
`size: 'lg' | 'md' | 'sm'`
and can be handled in Modal to get the width using getWidth which I mentioned earlier.

Implementation of getWidth
```
const getWidth = useMemo(() => {
        if (size === 'lg') return 'w-11/12 md:11/12 lg:w-11/12';
        if (size === 'md') return 'w-11/12 md:8/12 lg:w-1/2';
        if (size === 'sm') return 'w-11/12 md:1/2 lg:w-1/4';
    }, [size])
```
For each size I have three style classes. This is to achieve responsive design on Modal.
The first className without any prefix is for mobile screen and tailwind is mobile first framework.
the prefixes md and lg represents medium screen size (tabs) and large screen size (desktops) respecitively.

when size is lg - the width should be maximum, hence md and lg widths' are almost full.
when size is md - the width should be lesser than lg but sufficiently bigger.
when size is sm - this is for small modal window and width values are least.

In all three sizes, for mobile screen, the width can be maximum so that the content is visible fully.

Modal component is now designed. Below is how we can call it.
```
<Modal show={showPopup} size={'sm'}>
          <Modal.Header>This is Header</Modal.Header>
          <Modal.Content>Testing</Modal.Content>
          <Modal.Footer>
          <button className={`bg-blue-700 p-2 text-white 
          rounded hover:bg-blue-500`}>OK</button>
          <button
            className="bg-red-700 p-2 
          text-white rounded 
          hover:bg-red-500" onClick={() => setShowPopup(false)}>Cancel</button>
          </Modal.Footer>
      </Modal>
```

Modal Component complete code is as below.
```
import React, { ReactNode, useMemo } from "react";

const Modal = ({ show, children, size }: { show: boolean, children: ReactNode | any, size: 'lg' | 'md' | 'sm' }) => {

    const getWidth = useMemo(() => {
        if (size === 'lg') return 'w-11/12 md:11/12 lg:w-11/12';
        if (size === 'md') return 'w-11/12 md:8/12 lg:w-1/2';
        if (size === 'sm') return 'w-11/12 md:1/2 lg:w-1/4';
    }, [size])

    return <>{show && <div className={"flex justify-center items-center fixed top-0 w-screen h-screen bg-gray-500 bg-opacity-50"}>
        <div className={`min-h-[400px] ${getWidth} bg-white`}>
            {children}
        </div>
    </div>}</>
}

const Header = ({children}: {children: ReactNode | any}) => {
    return <div className={"p-2 text-[20px] font-bold border-b border-b-gray-500"}>
        {children}
</div>
}

const Content = ({children}: {children: ReactNode | any}) => {
    return <div className={"p-2 min-h-[300px] overflow-auto"}>
        {children}
</div>
}

const Footer = ({children}: {children: ReactNode | any}) => {
    return <div className={"p-2 border-t border-t-gray-500"}>
    {children}
</div>
}

Modal.Header = Header;
Modal.Content = Content;
Modal.Footer = Footer;

export default Modal;
```

In Modal component, we can have one more prop which can be callback function. This callback can be called when the modal is closed to achieve any effect after closing from the parent.

I have this code in my github repo (develop branch).
{% embed https://github.com/pramodkumar115/tailwindcss-in-react %}

That is all we need to get a Modal popup component created in React using Tailwind CSS. It can be used in any part of the application now on.

Hope this helps in your web development. Happy coding!