Sign-up form
'use client';
import { useState, type FormEvent } from 'react';import { Button, Card, Checkbox, Field, Stack, Text, TextInput } from '@helixui/core';
export interface SignUpFormProps { onSubmit: (creds: { email: string; password: string }) => Promise<void>;}
export function SignUpForm({ onSubmit }: SignUpFormProps) { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [agree, setAgree] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState<string | null>(null);
const passwordsMatch = password.length >= 8 && password === confirm; const canSubmit = email && passwordsMatch && agree && !busy;
const submit = async (e: FormEvent) => { e.preventDefault(); setError(null); if (!passwordsMatch) { setError('Passwords don\'t match.'); return; } setBusy(true); try { await onSubmit({ email, password }); } catch (err) { setError(err instanceof Error ? err.message : 'Sign-up failed.'); } finally { setBusy(false); } };
return ( <Card variant="elevated" style={{ padding: 'var(--helixui-space-8)', maxWidth: 400 }}> <form onSubmit={submit}> <Stack gap={4}> <Text size="2xl" weight="semibold">Create an account</Text> <Field label="Email"> <TextInput type="email" autoComplete="email" value={email} onChange={setEmail} required /> </Field> <Field label="Password" hint="At least 8 characters."> <TextInput type="password" autoComplete="new-password" value={password} onChange={setPassword} required minLength={8} /> </Field> <Field label="Confirm password"> <TextInput type="password" autoComplete="new-password" value={confirm} onChange={setConfirm} required /> </Field> <Checkbox isSelected={agree} onChange={setAgree}> I agree to the <a href="/terms">terms</a> and <a href="/privacy">privacy policy</a>. </Checkbox> {error ? <Text size="sm" tone="danger">{error}</Text> : null} <Button type="submit" loading={busy} disabled={!canSubmit}>Create account</Button> </Stack> </form> </Card> );}The “disabled until everything’s valid” UX matches Apple’s HIG and
keeps you out of the “user pressed submit and nothing happened”
failure mode. The canSubmit check is reactive — flip any field and
the button enables.