How to Build an Auth Flow in React (Login, Signup, OTP, Reset)
A complete guide to building authentication flows in React: login, signup, OTP verification, and password reset — all accessible, all with Ninna UI components.
By Ninna UI Team
Authentication flows are one of the most common UI patterns — and one of the most accessibility-critical. Every form needs labels, error messages, focus management, and keyboard navigation. Let's build a complete auth flow with Ninna UI.
1. Login form
import { Button, Heading } from "@ninna-ui/primitives";
import { Input, Field, Checkbox } from "@ninna-ui/forms";
import { VStack } from "@ninna-ui/layout";
export function LoginForm() {
return (
<VStack gap="4" as="form">
<Heading as="h1" size="2xl">Welcome back</Heading>
<Field label="Email" htmlFor="email" error="Invalid email" invalid>
<Input id="email" type="email" placeholder="you@example.com" />
</Field>
<Field label="Password" htmlFor="password">
<Input id="password" type="password" />
</Field>
<Checkbox>Remember me</Checkbox>
<Button type="submit" color="primary" fullWidth>Log in</Button>
</VStack>
);
}The Field component automatically wires aria-labelledby, aria-describedby, and aria-invalid to the input. You don't need to add any ARIA attributes manually.
2. Signup form
export function SignupForm() {
return (
<VStack gap="4" as="form">
<Heading as="h1" size="2xl">Create account</Heading>
<Field label="Name" htmlFor="name">
<Input id="name" placeholder="Jane Doe" />
</Field>
<Field label="Email" htmlFor="email">
<Input id="email" type="email" />
</Field>
<Field label="Password" htmlFor="password" description="At least 8 characters">
<Input id="password" type="password" />
</Field>
<Button type="submit" color="primary" fullWidth>Sign up</Button>
</VStack>
);
}3. OTP verification
Use the PinInput component for OTP codes. It handles individual digit inputs, auto-advance, and paste:
import { PinInput } from "@ninna-ui/forms";
export function OtpVerify() {
return (
<VStack gap="4" as="form">
<Heading as="h1" size="2xl">Verify your email</Heading>
<Text>Enter the 6-digit code we sent to your email.</Text>
<PinInput length={6} />
<Button type="submit" color="primary">Verify</Button>
</VStack>
);
}4. Password reset
export function ResetPassword() {
return (
<VStack gap="4" as="form">
<Heading as="h1" size="2xl">Reset password</Heading>
<Field label="New password" htmlFor="password" description="At least 8 characters">
<Input id="password" type="password" />
</Field>
<Field label="Confirm password" htmlFor="confirm" error="Passwords do not match" invalid>
<Input id="confirm" type="password" />
</Field>
<Button type="submit" color="primary">Reset password</Button>
</VStack>
);
}5. Ready-made auth blocks
Ninna UI ships production-ready auth blocks you can copy: login, signup, magic link, OTP verify, reset password, and social wall. Browse them at /blocks and filter by the Authentication category.
These blocks include responsive layout, social login buttons, and consistent styling — all accessible by default. Copy the code and adapt it to your auth provider.