Not affiliated with official shadcn/ui. Visit ui.shadcn.com for official docs.
Unlock this block—Get Pro at 60% offReact Dialog Block Contact Support
Contact support dialog with issue category selection, priority level, message input, file attachment, and ticket submission
Looking to implement shadcn/ui blocks?
Join our Discord community for help from other developers.
Get help from support with structured ticket submission. This React contact support dialog provides issue category dropdown for routing, priority level selection, detailed message textarea, file attachment for screenshots or logs, and ticket confirmation with reference number. Built with shadcn/ui Dialog, Select, Textarea, Button, and Input components using Tailwind CSS, users submit support requests efficiently with all necessary context. Select category, describe issue, attach files—perfect for SaaS applications, customer portals, help desks, or any Next.js application needing structured customer support ticket creation.
"use client";import { useState } from "react";import { MessageSquare, Send, Paperclip, X, Loader2, Check, AlertTriangle, HelpCircle, Bug, CreditCard, Settings, FileText,} from "lucide-react";import { Button } from "@/components/ui/button";import { Badge } from "@/components/ui/badge";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 { Textarea } from "@/components/ui/textarea";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";import { Separator } from "@/components/ui/separator";import { cn } from "@/lib/utils";type Category = { id: string; label: string; icon: React.ElementType;};const categories: Category[] = [ { id: "technical", label: "Technical Issue", icon: Bug }, { id: "billing", label: "Billing & Payments", icon: CreditCard }, { id: "account", label: "Account & Access", icon: Settings }, { id: "feature", label: "Feature Question", icon: HelpCircle }, { id: "other", label: "General Inquiry", icon: FileText },];type Priority = "low" | "medium" | "high" | "urgent";const priorities: { value: Priority; label: string; description: string }[] = [ { value: "low", label: "Low", description: "General questions, no urgency" }, { value: "medium", label: "Medium", description: "Issue affecting work, workaround exists" }, { value: "high", label: "High", description: "Significant impact, needs attention" }, { value: "urgent", label: "Urgent", description: "Critical blocker, production issue" },];type Attachment = { name: string; size: number;};export const title = "React Dialog Block Contact Support";export default function DialogContactSupport() { const [open, setOpen] = useState(false); const [category, setCategory] = useState<string>(""); const [priority, setPriority] = useState<Priority>("medium"); const [subject, setSubject] = useState(""); const [message, setMessage] = useState(""); const [attachments, setAttachments] = useState<Attachment[]>([]); const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false); const [ticketId, setTicketId] = useState(""); const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { const files = e.target.files; if (files) { const newAttachments = Array.from(files).map((file) => ({ name: file.name, size: file.size, })); setAttachments((prev) => [...prev, ...newAttachments]); } e.target.value = ""; }; const removeAttachment = (index: number) => { setAttachments((prev) => prev.filter((_, i) => i !== index)); }; const formatFileSize = (bytes: number) => { if (bytes < 1024) return bytes + " B"; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB"; return (bytes / (1024 * 1024)).toFixed(1) + " MB"; }; const handleSubmit = async () => { if (!category || !subject || !message) return; setIsSubmitting(true); await new Promise((resolve) => setTimeout(resolve, 1500)); setTicketId(`SUP-${Math.random().toString(36).substring(2, 8).toUpperCase()}`); setIsSubmitting(false); setIsSubmitted(true); }; const handleClose = () => { setOpen(false); setTimeout(() => { setCategory(""); setPriority("medium"); setSubject(""); setMessage(""); setAttachments([]); setIsSubmitted(false); setTicketId(""); }, 200); }; const isValid = category && subject.trim().length >= 5 && message.trim().length >= 20; if (isSubmitted) { return ( <Dialog open={open} onOpenChange={setOpen}> <div className="flex min-h-[350px] items-center justify-center"> <DialogTrigger asChild> <Button variant="outline"> <MessageSquare className="mr-2 h-4 w-4" /> Contact Support </Button> </DialogTrigger> </div> <DialogContent className="sm:max-w-md"> <div className="flex flex-col items-center justify-center py-8 text-center"> <div className="flex h-12 w-12 items-center justify-center rounded-full border-2 border-primary"> <Check className="h-6 w-6 text-primary" /> </div> <h3 className="mt-4 text-lg font-semibold">Ticket Submitted</h3> <p className="mt-1 text-sm text-muted-foreground"> We've received your support request and will respond soon. </p> <div className="mt-4 rounded-lg border p-4 w-full"> <p className="text-xs text-muted-foreground">Ticket Reference</p> <p className="font-mono text-lg font-semibold">{ticketId}</p> <p className="mt-2 text-xs text-muted-foreground"> Expected response within 24 hours </p> </div> <p className="mt-4 text-xs text-muted-foreground"> A confirmation email has been sent to your registered email address. </p> <Button onClick={handleClose} className="mt-6"> Done </Button> </div> </DialogContent> </Dialog> ); } return ( <Dialog open={open} onOpenChange={setOpen}> <div className="flex min-h-[350px] items-center justify-center"> <DialogTrigger asChild> <Button variant="outline"> <MessageSquare className="mr-2 h-4 w-4" /> Contact Support </Button> </DialogTrigger> </div> <DialogContent className="sm:max-w-lg"> <DialogHeader> <DialogTitle className="flex items-center gap-2"> <MessageSquare className="h-5 w-5" /> Contact Support </DialogTitle> <DialogDescription> Describe your issue and we'll get back to you as soon as possible. </DialogDescription> </DialogHeader> <div className="space-y-4 py-4"> {/* Category */} <div className="space-y-2"> <Label> Category <span className="text-destructive">*</span> </Label> <Select value={category} onValueChange={setCategory}> <SelectTrigger> <SelectValue placeholder="Select issue category" /> </SelectTrigger> <SelectContent> {categories.map((cat) => ( <SelectItem key={cat.id} value={cat.id}> <div className="flex items-center gap-2"> <cat.icon className="h-4 w-4" /> {cat.label} </div> </SelectItem> ))} </SelectContent> </Select> </div> {/* Priority */} <div className="space-y-2"> <Label>Priority</Label> <Select value={priority} onValueChange={(v) => setPriority(v as Priority)}> <SelectTrigger> <SelectValue /> </SelectTrigger> <SelectContent> {priorities.map((p) => ( <SelectItem key={p.value} value={p.value}> <div className="flex items-center gap-2"> <Badge variant={p.value === "urgent" ? "destructive" : "secondary"} className="text-xs" > {p.label} </Badge> <span className="text-xs text-muted-foreground"> {p.description} </span> </div> </SelectItem> ))} </SelectContent> </Select> </div> <Separator /> {/* Subject */} <div className="space-y-2"> <Label htmlFor="subject"> Subject <span className="text-destructive">*</span> </Label> <Input id="subject" placeholder="Brief summary of your issue" value={subject} onChange={(e) => setSubject(e.target.value)} /> </div> {/* Message */} <div className="space-y-2"> <Label htmlFor="message"> Description <span className="text-destructive">*</span> </Label> <Textarea id="message" placeholder="Please describe your issue in detail. Include any error messages, steps to reproduce, and what you expected to happen." value={message} onChange={(e) => setMessage(e.target.value)} rows={4} /> <p className="text-xs text-muted-foreground"> {message.length < 20 ? `At least ${20 - message.length} more characters needed` : `${message.length} characters`} </p> </div> {/* Attachments */} <div className="space-y-2"> <Label>Attachments</Label> <div className="flex flex-wrap gap-2"> {attachments.map((file, index) => ( <div key={index} className="flex items-center gap-2 rounded-md border px-2 py-1 text-sm" > <Paperclip className="h-3 w-3 text-muted-foreground" /> <span className="max-w-[150px] truncate">{file.name}</span> <span className="text-xs text-muted-foreground"> ({formatFileSize(file.size)}) </span> <Button variant="ghost" size="icon" className="h-4 w-4 p-0" onClick={() => removeAttachment(index)} > <X className="h-3 w-3" /> </Button> </div> ))} <label className="flex cursor-pointer items-center gap-2 rounded-md border border-dashed px-3 py-1 text-sm text-muted-foreground hover:border-primary hover:text-primary transition-colors"> <Paperclip className="h-4 w-4" /> Add file <input type="file" className="hidden" multiple accept="image/*,.pdf,.txt,.log" onChange={handleFileChange} /> </label> </div> <p className="text-xs text-muted-foreground"> Screenshots and log files help us resolve issues faster. Max 10MB per file. </p> </div> {/* Priority Warning */} {priority === "urgent" && ( <div className="flex items-start gap-2 rounded-lg border border-destructive/50 p-3"> <AlertTriangle className="h-4 w-4 text-destructive mt-0.5" /> <div> <p className="text-sm font-medium text-destructive"> Urgent Priority </p> <p className="text-xs text-muted-foreground"> Please only use urgent for production-blocking issues. Misuse may delay response to critical tickets. </p> </div> </div> )} </div> <DialogFooter> <Button variant="outline" onClick={handleClose}> Cancel </Button> <Button onClick={handleSubmit} disabled={!isValid || isSubmitting}> {isSubmitting ? ( <> <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Submitting... </> ) : ( <> <Send className="mr-2 h-4 w-4" /> Submit Ticket </> )} </Button> </DialogFooter> </DialogContent> </Dialog> );}Installation
npx shadcn@latest add https://www.shadcn.io/registry/dialog-contact-support.jsonnpx shadcn@latest add https://www.shadcn.io/registry/dialog-contact-support.jsonpnpm dlx shadcn@latest add https://www.shadcn.io/registry/dialog-contact-support.jsonbunx shadcn@latest add https://www.shadcn.io/registry/dialog-contact-support.jsonRelated blocks you will also like
React Dialog Block Feature Request
Feature suggestions
React Dialog Block Report Content
Report issues
React Dialog Block Feedback Rating
User feedback
React Dialog Block Media Upload
File attachments
React Dialog Block Success Confirmation
Ticket submitted
React Dialog Block API Error
Error reporting