Shadcn Dialogs for React & Next.js Websites

Shadcn Dialogs for React & Next.js Websites

posted 7 min read

Dialog (modal) components are a core part of modern websites whether it’s collecting emails, onboarding users, or handling quick actions without leaving the page.

From newsletter popups to onboarding flows, dialogs are used everywhere. If you’re building with React, Next.js, and shadcn/ui, you don’t need to build them from scratch anymore.

In this post, we’ll explore some ready-to-use shadcn dialog components you can plug into your projects.


Why Use Dialog Components

Shadcn Dialog components help keep users focused without breaking the flow of your react website.

Instead of navigating to new pages, users can:

  • Complete actions instantly
  • Interact with forms
  • Manage data quickly

This shadcn dialog box leads to:

  • Better UX
  • Faster interactions
  • Higher conversions

Got a shadcn dialog component to share?

Feel free to reach out on
LinkedIn,
Twitter,
Peerlist, or anywhere you prefer;
I’d love to check it out and add it to this list.


1. Newsletter Subscription Shadcn Dialog

A modern dialog designed for capturing email subscriptions with a clean and conversion-focused layout.

Newsletter Subscription Shadcn Dialog

Features

  • Hero-style layout
  • Email input field
  • Optional opt-out checkbox
  • Clear CTA

Tech Stack

React, Next.js, shadcn/ui

Get Code

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";

const DialogBlock = () => {
  return (
    <section className="bg-primary dark:bg-background lg:py-20 sm:py-16 py-8">
      <div className="max-w-7xl xl:px-16 lg:px-8 px-4 mx-auto">
        <Dialog defaultOpen>
          <DialogTrigger>
            <div className="bg-background px-2.5 py-1.5 rounded-md border border-border text-sm font-semibold">
              Open Dialog
            </div>
          </DialogTrigger>
          <DialogContent className="md:max-w-4xl p-0 rounded-none">
            <DialogHeader className="sr-only">
              <DialogTitle>Newsletter Popup</DialogTitle>
            </DialogHeader>
            <div className="flex md:flex-row flex-col">
              <div className="md:max-w-md w-full">
                <img
                  src="https://images.shadcnspace.com/assets/backgrounds/newsletter-image.webp"
                  alt="newsletter-image"
                  className="w-full object-cover sm:h-full h-40"
                />
              </div>
              <div className="md:p-16 p-6 w-full ">
                <div className="flex flex-col gap-6">
                  <div className="flex flex-col gap-4">
                    <h2 className="text-card-foreground text-3xl font-medium">
                      Subscribe to the latest updates of Shadcn Space
                    </h2>
                    <p className="text-muted-foreground text-base font-normal">
                      Subscribe our newsletters and get the latest business
                      updates
                    </p>
                  </div>
                  <form className="flex flex-col gap-4">
                    <div className="flex flex-col gap-3">
                      <Input
                        id="email"
                        type="email"
                        placeholder="*Emails are not allowed*"
                        required
                        className="dark:bg-background rounded-lg h-9 shadow-xs"
                      />
                      <Button
                        type="submit"
                        size={"lg"}
                        className="rounded-lg h-10 cursor-pointer hover:bg-primary/80"
                      >
                        Subscribe now
                      </Button>
                    </div>
                    <div className="flex flex-row items-center justify-between w-full">
                      <div className="flex items-center gap-3 ">
                        <Checkbox id="newsletter" className="cursor-pointer" />
                        <FieldLabel
                          htmlFor="newsletter"
                          className="text-sm text-primary font-normal cursor-pointer"
                        >
                          Don't show this popup again
                        </FieldLabel>
                      </div>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </DialogContent>
        </Dialog>
      </div>
    </section>
  );
};

export default DialogBlock;

2. Simple Shadcn Dialog Box (Classic)

A minimal and familiar dialog UI inspired by traditional modal designs.

Simple Shadcn Dialog Box

Features

  • Clean layout
  • Smooth animations
  • Accessible structure

Tech Stack

Built using Radix UI Dialog primitive and styled with Tailwind CSS

Get Code

npx shadcn@latest add dialog

3. Add a Writer Shadcn Dialog

A practical dialog component for adding contributors or writers inside an application.

Add a Writer Shadcn Dialog

Use Cases

  • CMS platforms
  • Blogging tools
  • LMS dashboards

Features

  • Simple input structure
  • Clean UI
  • Easy data handling

Get Code

"use client";

import { Plus, UserRoundIcon, X } from "lucide-react";
import { useRef, useState } from "react";

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export default function Dialog12() {
  const [open, setOpen] = useState(true);
  const [authorName, setAuthorName] = useState("Ephraim Duncan");
  const [title, setTitle] = useState("Design Engineer");
  const [image, setImage] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      if (file.size > 1048576) {
        alert("File size exceeds 1MB limit");
        return;
      }

      const reader = new FileReader();
      reader.onload = (event) => {
        setImage(event.target?.result as string);
      };
      reader.readAsDataURL(file);
    }
  };

  const triggerFileInput = () => {
    fileInputRef.current?.click();
  };

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        <Button>Open Dialog</Button>
      </DialogTrigger>
      <DialogContent className="sm:max-w-lg p-0 rounded-3xl gap-0">
        <DialogHeader className="border-b px-6 py-4">
          <DialogTitle className="text-balance font-medium">Add a writer</DialogTitle>
        </DialogHeader>

        <div className="grid grid-cols-1 md:grid-cols-5 px-6 pt-4 pb-6">
          <div className="flex flex-col items-center justify-center  md:col-span-2">
            <div className="relative mb-2">
              <Avatar className="h-24 w-24 border-2 border-muted">
                <AvatarImage src={image || undefined} alt="Profile" />
                <AvatarFallback>
                  <UserRoundIcon
                    size={52}
                    className="text-muted-foreground"
                    aria-hidden="true"
                  />
                </AvatarFallback>
              </Avatar>
              <Button
                variant="ghost"
                size="icon-sm"
                className="absolute -top-0.5 -right-0.5 bg-accent rounded-full border-[3px] border-background hover:bg-accent"
                onClick={() => {
                  if (image) {
                    setImage(null);
                    if (fileInputRef.current) {
                      fileInputRef.current.value = "";
                    }
                  } else {
                    triggerFileInput();
                  }
                }}
              >
                {image ? (
                  <X className="h-4 w-4 text-muted-foreground" />
                ) : (
                  <Plus className="h-3 w-3 text-muted-foreground" />
                )}
                <span className="sr-only">
                  {image ? "Remove image" : "Upload image"}
                </span>
              </Button>
            </div>

            <p className="text-pretty text-center font-medium">Upload Image</p>
            <p className="text-pretty text-center text-sm text-muted-foreground">
              Max file size: 1MB
            </p>
            <input
              type="file"
              ref={fileInputRef}
              onChange={handleFileChange}
              accept="image/*"
              className="hidden"
            />
            <Button
              variant="outline"
              size="sm"
              className="mt-2"
              onClick={triggerFileInput}
            >
              Add Image
            </Button>
          </div>

          <div className="flex flex-col justify-between md:col-span-3">
            <div className="space-y-4">
              <div className="space-y-1">
                <Label htmlFor="author-name" className="flex items-center">
                  Author name <span className="text-primary">*</span>
                </Label>
                <Input
                  id="author-name"
                  value={authorName}
                  onChange={(e) => setAuthorName(e.target.value)}
                  required
                />
              </div>

              <div className="space-y-1">
                <div className="flex items-center">
                  <Label htmlFor="title">Title</Label>
                </div>
                <Input
                  id="title"
                  value={title}
                  onChange={(e) => setTitle(e.target.value)}
                />
              </div>
            </div>

            <div className="flex justify-end gap-2">
              <Button variant="outline" onClick={() => setOpen(false)}>
                Cancel
              </Button>
              <Button className="bg-foreground text-background hover:bg-foreground/90">
                Save Changes
              </Button>
            </div>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
}

4. Onboarding Shadcn Dialog

A dialog built for guiding users through onboarding steps.

Onboarding Shadcn Dialog

Features

  • Step-based flow
  • Smooth transitions
  • User-friendly interface

Tech Stack

React, Tailwind, shadcn/ui

Get Code

import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";

export const title = "Signup Form";

const Example = () => (
  <Dialog>
    <DialogTrigger asChild>
      <Button variant="outline">Sign Up</Button>
    </DialogTrigger>
    <DialogContent className="sm:max-w-md">
      <DialogHeader>
        <DialogTitle>Create an account</DialogTitle>
        <DialogDescription>
          Enter your details below to create your account.
        </DialogDescription>
      </DialogHeader>
      <div className="space-y-4">
        <div className="space-y-2">
          <Label htmlFor="name">Full name</Label>
          <Input id="name" placeholder="John Doe" />
        </div>
        <div className="space-y-2">
          <Label htmlFor="email">Email</Label>
          <Input id="email" placeholder="*Emails are not allowed*" type="email" />
        </div>
        <div className="space-y-2">
          <Label htmlFor="password">Password</Label>
          <Input id="password" type="password" />
        </div>
        <Button className="w-full">Create Account</Button>
        <div className="relative flex items-center gap-2">
          <Separator className="flex-1" />
          <span className="shrink-0 px-2 text-xs text-muted-foreground uppercase">
            Or continue with
          </span>
          <Separator className="flex-1" />
        </div>
        <Button className="w-full" variant="outline">
          <svg className="mr-2 h-4 w-4" viewBox="0 0 24 24">
            <path
              d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
              fill="#4285F4"
            />
            <path
              d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
              fill="#34A853"
            />
            <path
              d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
              fill="#FBBC05"
            />
            <path
              d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
              fill="#EA4335"
            />
          </svg>
          Continue with Google
        </Button>
      </div>
      <DialogFooter className="sm:justify-center">
        <p className="text-sm text-muted-foreground">
          Already have an account?{" "}
          <button className="font-medium underline" type="button">
            Sign in
          </button>
        </p>
      </DialogFooter>
    </DialogContent>
  </Dialog>
);

export default Example;

5. NeoBrutalism Shadcn Dialog Box

A bold dialog design inspired by NeoBrutalism with strong visual styling.

NeoBrutalism Shadcn Dialog Box

Features

  • High contrast UI
  • Unique design style
  • Great for creative apps

Tech Stack

React, Tailwind, shadcn/ui

Get Code

import { Button } from "@/components/retroui/Button";
import { Dialog } from "@/components/retroui/Dialog";
import { Text } from "@/components/retroui/Text";
 
export default function DialogStyleDefault() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button>Open Dialog</Button>
      </Dialog.Trigger>
      <Dialog.Content>
        <Dialog.Header>
          <Text as="h5">Confirm your action?</Text>
        </Dialog.Header>
        <section className="flex flex-col gap-4 p-4">
          <section className="text-xl">
            <p>Are you sure you want to delete this item?</p>
            <p>This action cannout be undone.</p>
          </section>
          <section className="flex w-full justify-end">
            <Dialog.Trigger asChild>
              <Button>Confirm</Button>
            </Dialog.Trigger>
          </section>
        </section>
      </Dialog.Content>
    </Dialog>
  );
}

Final Thoughts

Dialog components are more than just popups they are essential UI building blocks.

Common Use Cases

  • Collecting emails
  • Onboarding users
  • Managing data
  • Displaying forms

Using pre-built shadcn components like these helps you:

  • Save development time
  • Maintain consistency
  • Ship faster

If you’re looking for more components, check out these shadcn UI blocks and explore the full shadcn library to speed up your workflow.

More Posts

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

8 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!