Getting Started
Blocks
A complete sign-up form component with email and password authentication, password confirmation validation, and built-in error handling for Better Auth.
About
The Better Auth Sign Up component is a fully-featured sign-up form designed to work seamlessly with Better Auth. It handles form validation, password confirmation, loading states, and error handling out of the box.
Features
- ✅ Email & Password Authentication: Complete sign-up flow with Better Auth
- ✅ Password Confirmation: Built-in password matching validation
- ✅ Form Validation: Zod schema validation with helpful error messages
- ✅ Error Handling: Automatic toast notifications for errors
- ✅ Loading States: Visual feedback during submission
- ✅ TypeScript Support: Fully typed for type safety
- ✅ Customizable: Props for title, description, and callbacks
- ✅ Responsive Design: Works on all screen sizes
Preview
Create an account
Enter your details to get started
"use client"
import { toast } from "sonner"
import { BetterAuthSignUp } from "@/registry/nowts/blocks/better-auth-signup/better-auth-signup"
export function BetterAuthSignupDemo() {
const handleSignUp = async (data: {
name: string
email: string
password: string
}) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
console.info("Sign up data:", data)
}
return (
<BetterAuthSignUp
onSignUp={handleSignUp}
onSuccess={() => {
toast.success("Account created successfully!")
}}
onError={(error) => {
toast.error(error)
}}
signInLink={
<>
Already have an account?{" "}
<a href="#" className="hover:text-foreground underline">
Sign in
</a>
</>
}
/>
)
}
Installation
1. Install the component
pnpm dlx shadcn@latest add https://ui.nowts.app/r/better-auth-signup.json
2. Configure Better Auth
import { createAuthClient } from "better-auth/react"
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
})
Usage
Basic Implementation
"use client"
import { authClient } from "@/lib/auth-client"
import { BetterAuthSignUp } from "@/components/better-auth-signup"
export default function SignUpPage() {
const handleSignUp = async (data: {
name: string
email: string
password: string
}) => {
const result = await authClient.signUp.email(data)
if (result.error) {
throw new Error(result.error.message)
}
}
return (
<BetterAuthSignUp
onSignUp={handleSignUp}
onSuccess={() => {
window.location.href = "/dashboard"
}}
/>
)
}
With Custom Messages
<BetterAuthSignUp
onSignUp={handleSignUp}
title="Join our platform"
description="Create your account in seconds"
onSuccess={() => {
toast.success("Welcome aboard!")
window.location.href = "/onboarding"
}}
onError={(error) => {
toast.error(`Sign up failed: ${error}`)
}}
/>
With Sign In Link
import Link from "next/link"
;<BetterAuthSignUp
onSignUp={handleSignUp}
signInLink={
<>
Already have an account?{" "}
<Link href="/signin" className="hover:text-foreground underline">
Sign in
</Link>
</>
}
/>
Server Action Pattern
import { BetterAuthSignUp } from "@/components/better-auth-signup"
import { signUpAction } from "./actions"
export default function SignUpPage() {
return (
<BetterAuthSignUp
onSignUp={async (data) => {
const result = await signUpAction(data)
if (!result.success) {
throw new Error(result.error)
}
}}
onSuccess={() => {
window.location.href = "/dashboard"
}}
/>
)
}
"use server"
import { auth } from "@/lib/auth"
export async function signUpAction(data: {
name: string
email: string
password: string
}) {
try {
await auth.api.signUpEmail({
body: {
email: data.email,
password: data.password,
name: data.name,
},
})
return { success: true }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Sign up failed",
}
}
}
API
BetterAuthSignUp Props
Prop | Type | Description | Default |
---|---|---|---|
onSignUp | (data: { name: string; email: string; password: string }) => Promise<void> | Required. Function to handle sign up | - |
onSuccess | () => void | Called after successful sign up | - |
onError | (error: string) => void | Called when sign up fails | - |
title | string | Card title | "Create an account" |
description | string | Card description | "Enter your details to get started" |
signInLink | React.ReactNode | Link to sign in page | - |
Validation Schema
The component uses the following Zod schema:
const signUpSchema = z
.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
password: z.string().min(8, "Password must be at least 8 characters"),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"],
})
Customization
Custom Validation
To use custom validation, you can create your own component based on this one:
"use client"
import { z } from "zod"
import { BetterAuthSignUp } from "@/components/better-auth-signup"
const customSchema = z
.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z
.string()
.email("Invalid email")
.endsWith("@company.com", "Must use company email"),
password: z
.string()
.min(12, "Password must be at least 12 characters")
.regex(/[A-Z]/, "Must contain uppercase letter")
.regex(/[0-9]/, "Must contain number"),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"],
})
Styling
The component uses Tailwind CSS and shadcn/ui styling. You can customize it by:
- Modifying the component's className props
- Updating your theme in
tailwind.config.js
- Using CSS variables for colors
Best Practices
- Password Strength: The default minimum is 8 characters. Consider adding additional validation for stronger passwords.
- Email Verification: Implement email verification after sign up.
- Rate Limiting: Add rate limiting on the server to prevent abuse.
- HTTPS Only: Always use HTTPS in production for authentication.
- Error Messages: Keep error messages generic to prevent user enumeration.
- Redirect After Sign Up: Always redirect users to a meaningful page after successful sign up.
Examples
With Better Auth
"use client"
import { toast } from "sonner"
import { authClient } from "@/lib/auth-client"
import { BetterAuthSignUp } from "@/components/better-auth-signup"
export default function SignUpPage() {
return (
<div className="flex min-h-screen items-center justify-center">
<BetterAuthSignUp
onSignUp={async (data) => {
const result = await authClient.signUp.email({
email: data.email,
password: data.password,
name: data.name,
})
if (result.error) {
throw new Error(result.error.message)
}
}}
onSuccess={() => {
toast.success("Account created! Redirecting...")
setTimeout(() => {
window.location.href = "/dashboard"
}, 1000)
}}
signInLink={
<>
Already have an account?{" "}
<a href="/signin" className="hover:text-foreground underline">
Sign in
</a>
</>
}
/>
</div>
)
}
Troubleshooting
Form not submitting
- Verify Better Auth is properly configured
- Check that
onSignUp
function is async and handles errors - Ensure all required environment variables are set
Validation errors not showing
- The component uses inline error messages below each field
- Check browser console for validation errors
- Verify Zod schema is compatible with your requirements
Password confirmation not working
- The validation happens automatically via the Zod schema
- Check that both password fields are filled
- Error appears on the "Confirm Password" field
On This Page
AboutFeaturesPreviewInstallation1. Install the component2. Configure Better AuthUsageBasic ImplementationWith Custom MessagesWith Sign In LinkServer Action PatternAPIBetterAuthSignUp PropsValidation SchemaCustomizationCustom ValidationStylingBest PracticesExamplesWith Better AuthTroubleshootingForm not submittingValidation errors not showingPassword confirmation not workingBuild 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