Getting Started
Blocks
"use client"
import { toast } from "sonner"
import { z } from "zod"
import { Card } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { ExtendedForm, useZodForm } from "@/components/ui/extended-form"
import { LoadingButton } from "@/components/ui/submit-button"
const formSchema = z.object({
email: z.string().email("Invalid email address"),
message: z.string().min(10, "Message must be at least 10 characters"),
})
export function SubmitButtonDemo() {
const form = useZodForm({
schema: formSchema,
defaultValues: {
email: "",
message: "",
},
})
const onSubmit = async (data: z.infer<typeof formSchema>) => {
await new Promise((resolve) => setTimeout(resolve, 2000))
toast.success("Form submitted successfully!")
console.log(data)
form.reset()
}
return (
<Card className="p-6">
<ExtendedForm form={form} onSubmit={onSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
{...form.register("email")}
/>
{form.formState.errors.email && (
<p className="text-destructive text-sm">
{form.formState.errors.email.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="message">Message</Label>
<Input
id="message"
placeholder="Type your message here..."
{...form.register("message")}
/>
{form.formState.errors.message && (
<p className="text-destructive text-sm">
{form.formState.errors.message.message}
</p>
)}
</div>
<LoadingButton
type="submit"
className="w-full"
loading={form.formState.isSubmitting}
>
Submit Form
</LoadingButton>
</ExtendedForm>
</Card>
)
}
Installation
pnpm dlx shadcn@latest add https://ui.nowts.app/r/submit-button.json
Usage
The SubmitButton
automatically detects form submission state using React's useFormStatus
hook. It shows a loading spinner during form submission.
import { SubmitButton } from "@/components/ui/submit-button"
export function MyForm() {
return (
<form action={serverAction}>
<input name="email" type="email" />
<SubmitButton>Submit</SubmitButton>
</form>
)
}
With ExtendedForm
The button works seamlessly with ExtendedForm
for client-side validation:
import { z } from "zod"
import { ExtendedForm, useZodForm } from "@/components/ui/extended-form"
import { SubmitButton } from "@/components/ui/submit-button"
const schema = z.object({
email: z.string().email(),
})
export function MyForm() {
const form = useZodForm({ schema })
return (
<ExtendedForm
form={form}
onSubmit={async (data) => {
// Form submission
}}
>
<input {...form.register("email")} />
<SubmitButton>Submit</SubmitButton>
</ExtendedForm>
)
}
LoadingButton Variant
You can also use LoadingButton
for manual control of the loading state:
import { LoadingButton } from "@/components/ui/submit-button"
export function MyButton() {
const [isLoading, setIsLoading] = useState(false)
return (
<LoadingButton loading={isLoading} onClick={handleClick}>
Click Me
</LoadingButton>
)
}
Props
SubmitButton
Accepts all props from Button
component from shadcn/ui.
LoadingButton
Prop | Type | Default | Description |
---|---|---|---|
loading | boolean | false | Controls the loading state |
...buttonProps | ButtonProps | - | All button props are forwarded |
Build Your SaaS in Days, Not Months
NOW.TS is the Next.js 15 boilerplate with everything you need to launch your SaaS—auth, payments, database, and AI-ready infrastructure.
Learn more about NOW.TS