Contact Form
atroui/contact-formFreeBlock
Multi-step contact form with editable CONTENT; posts to your API.
Install it
npx shadcn@latest add https://atroui.com/r/contact-form.jsonSource
1"use client"23import { ArrowLeft, ArrowRight, Check, Loader2, Send } from "lucide-react"4import { AnimatePresence, motion, useReducedMotion } from "motion/react"5import { useState, type FormEvent } from "react"67/**8 * Edit CONTENT / PROJECT_TYPES to match your studio intake.9 * Posts JSON to CONTENT.endpoint (wire your own API route).10 * Steps reveal one at a time (Family: gradual revelation + fluid travel).11 */12const CONTENT = {13 stamp: "Contact",14 headline: "Tell us what you want to ship.",15 lede: "Four short steps. We reply within one business day.",16 endpoint: "/api/contact",17 successTitle: "Got it.",18 successBody: "We'll reply within one business day.",19 submitLabel: "Send",20}2122const PROJECT_TYPES = [23 { id: "mvp-sprint", label: "7-Day MVP Sprint", hint: "from $4,800" },24 { id: "ai-integration", label: "AI Feature", hint: "from $2,400" },25 { id: "design-system", label: "Design System", hint: "from $3,600" },26 { id: "other", label: "Something else", hint: "Describe it below" },27] as const2829const BUDGETS = [30 { id: "<2k", label: "Under $2k" },31 { id: "2k-5k", label: "$2k - $5k" },32 { id: "5k-10k", label: "$5k - $10k" },33 { id: "10k+", label: "$10k+" },34 { id: "unsure", label: "Not sure yet" },35] as const3637const TIMELINES = [38 { id: "asap", label: "ASAP · 1-2 weeks" },39 { id: "1-month", label: "Within a month" },40 { id: "flexible", label: "Flexible" },41 { id: "unsure", label: "Not sure yet" },42] as const4344const STEPS = [45 { id: "who", label: "Who", title: "Who should we reply to?" },46 { id: "what", label: "What", title: "What are we scoping?" },47 { id: "when", label: "When", title: "Timing & budget" },48 { id: "send", label: "Send", title: "Look right?" },49] as const5051type Status =52 | { kind: "idle" }53 | { kind: "submitting" }54 | { kind: "success" }55 | { kind: "error"; message: string }5657function Chip({58 pressed,59 onClick,60 disabled,61 children,62}: {63 pressed: boolean64 onClick: () => void65 disabled?: boolean66 children: React.ReactNode67}) {68 return (69 <button70 type="button"71 onClick={onClick}72 aria-pressed={pressed}73 disabled={disabled}74 className={75 pressed76 ? "border border-[var(--color-brand,#0b7bff)] bg-[var(--color-brand,#0b7bff)]/10 px-3.5 py-2.5 text-left text-sm text-foreground"77 : "border border-border-subtle bg-background px-3.5 py-2.5 text-left text-sm text-muted-foreground hover:border-border hover:text-foreground"78 }79 >80 {children}81// … truncated
What it pulls in
npm packages
Files it writes
More from atroui
All 82 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Analytics Provideranalytics-provider | atroui | Blocks | Free | 1 dep | |
| AR Portfolioar-portfolio | atroui | Blocks | Free | 1 dep | |
| Before / Afterbefore-after-slider | atroui | Blocks | Free | no deps | |
| Calendly Embedcalendly-embed | atroui | Blocks | Free | 1 dep | |
| Changelogchangelog | atroui | Blocks | Free | no deps | |
| Command Menucommand-menu | atroui | Blocks | Free | 4 deps | |
| Contextual CTAcontextual-cta | atroui | Blocks | Free | 2 deps | |
| Count Upcount-up | atroui | Blocks | Free | no deps |
