How to Create a Reusable Modal in Next.js Using Context

Candra Kriswinarto
4 min readJan 31, 2025

--

Photo by Lautaro Andreani on Unsplash

In this article, we’ll walk through how to create a reusable modal in Next.js using React Context. This approach allows you to trigger the modal from any component in your application without having to pass props down multiple levels. We’ll also use shadcn/ui for the modal and button components.

Step 1: Setting Up the Modal Provider

First, let’s create a context provider to manage the modal’s state. This provider will allow any component in your app to open or close the modal.

Create a file components/modals/providers.tsx and paste the following code:

"use client";

import { createContext, Dispatch, SetStateAction } from "react";
import { useSignInModal } from "./sign-in-modal";
// Create a context for the modal
export const ModalContext = createContext<{
setShowSignInModal: Dispatch<SetStateAction<boolean>>;
}>({
setShowSignInModal: () => {},
});
// ModalProvider component to wrap your app with
export default function ModalProvider({
children,
}: Readonly<{ children: React.ReactNode }>) {
const { setShowSignInModal, SignInModal, showSignInModal } = useSignInModal();
return (
<ModalContext.Provider
value={{
setShowSignInModal,
}}
>
{showSignInModal && <SignInModal />}
{children}
</ModalContext.Provider>
);
}

Explanation:

  1. ModalContext: This is a React context that holds the state and function to control the modal. It provides setShowSignInModal to any component that consumes it.
  2. ModalProvider: This is a wrapper component that provides the context to its children. It uses the useSignInModal hook (which we'll define next) to manage the modal's state and render the modal when showSignInModal is true.

Step 2: Creating the Modal UI

Next, let’s create the modal component. We’ll use shadcn/ui for the Dialog and Button components.

Create a file components/modals/sign-in-modal.tsx and paste the following code:

"use client";

import { Dispatch, SetStateAction, useState } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "../ui/button";
import { IceCream, Loader2 } from "lucide-react";
// SignInModal component
function SignInModal({
showSignInModal,
setShowSignInModal,
}: {
showSignInModal: boolean;
setShowSignInModal: Dispatch<SetStateAction<boolean>>;
}) {
const [signInClicked, setSignInClicked] = useState(false);
return (
<Dialog open={showSignInModal} onOpenChange={setShowSignInModal}>
<DialogContent>
<DialogHeader>
<DialogTitle className="text-center">Welcome to WebApp</DialogTitle>
<DialogDescription className="text-center">
Sign in to your account to continue
</DialogDescription>
</DialogHeader>
<Button
variant="outline"
className="w-full"
disabled={signInClicked}
onClick={() => {
setSignInClicked(true);
setTimeout(() => {
setShowSignInModal(false);
setSignInClicked(false);
}, 1000);
}}
>
{signInClicked ? (
<Loader2 className="mr-2 size-4 animate-spin" />
) : (
<IceCream className="mr-2 size-4" />
)}
Sign In with Google
</Button>
</DialogContent>
</Dialog>
);
}
// useSignInModal hook to manage modal state
export function useSignInModal() {
const [showSignInModal, setShowSignInModal] = useState(false);
const SignInModalComponent = () => {
return (
<SignInModal
showSignInModal={showSignInModal}
setShowSignInModal={setShowSignInModal}
/>
);
};
return {
setShowSignInModal,
showSignInModal,
SignInModal: SignInModalComponent,
};
}

Explanation:

  1. SignInModal: This is the modal component. It uses shadcn/ui's Dialog component to create a modal with a title, description, and a button. The button simulates a sign-in process with a loading state.
  2. useSignInModal: This is a custom hook that manages the state of the modal (showSignInModal) and provides a function (setShowSignInModal) to toggle the modal. It also returns the SignInModal component.

Step 3: Installing shadcn/ui Components

To use the Dialog and Button components from shadcn/ui, you need to install them:

  1. Install shadcn/ui (if not already installed):
npx shadcn-ui@latest init

2. Add the Dialog and Button components:

npx shadcn-ui@latest add dialog button

Step 4: Using the Modal in a Component

Now that the modal and provider are set up, you can trigger the modal from any component. Let’s create a Navbar component that opens the modal when a button is clicked.

Create a file components/navbar.tsx and paste the following code:

"use client";

import Link from "next/link";
import { Button } from "./ui/button";
import { useContext } from "react";
import { ModalContext } from "./modals/providers";
export default function Navbar() {
const { setShowSignInModal } = useContext(ModalContext);
return (
<div className="border-b">
<div className="h-14 container mx-auto flex items-center justify-between">
<Link href="/">Logo</Link>
<Button onClick={() => setShowSignInModal(true)}>Login</Button>
</div>
</div>
);

Explanation:

  1. Navbar: This component uses the ModalContext to access the setShowSignInModal function. When the "Login" button is clicked, it sets showSignInModal to true, which opens the modal.

Step 5: Wrapping Your App with the Modal Provider

Finally, wrap your app (or the part of your app where you want the modal to be available) with the ModalProvider. For example, in app/layout.tsx:

import { ModalProvider } from "@/components/modals/providers";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<ModalProvider>{children}</ModalProvider>
</body>
</html>
);
}

Summary

  1. ModalProvider: Manages the modal's state and provides it to the rest of the app.
  2. SignInModal: The UI for the modal.
  3. useSignInModal: A custom hook to manage the modal's state.
  4. Navbar: A component that triggers the modal using the context.

With this setup, you can easily trigger the modal from any component by importing the context and calling setShowSignInModal.

If you’d like to see a video tutorial, check out this link.

--

--

Candra Kriswinarto
Candra Kriswinarto

Written by Candra Kriswinarto

👋 Front-End Developer & YouTuber. I write about web dev, share coding tips, and create tutorials to help others learn. https://youtube.com/@CandDev

No responses yet