Use Case
Build an Auth Flow with React and Tailwind CSS v4
Build a complete authentication flow in React: login, signup, OTP verification, and password reset — all accessible, all with Ninna UI components.
Why accessibility matters for auth forms
Auth forms are the first thing users see. Every form needs labels, error messages, focus management, and keyboard navigation. Ninna UI's Field component auto-wires aria-labelledby, aria-describedby, and aria-invalid to the input — you just pass label, description, and error props.
1. Login form
Use Field, Input from @ninna-ui/forms and Button from @ninna-ui/primitives:
import { Button, Heading } from "@ninna-ui/primitives";
import { Input, Field, Checkbox } from "@ninna-ui/forms";
import { VStack } from "@ninna-ui/layout";
<VStack gap="4" as="form">
<Heading as="h1" size="2xl">Welcome back</Heading>
<Field label="Email" htmlFor="email">
<Input id="email" type="email" />
</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>2. OTP verification with PinInput
Use PinInput for 6-digit OTP codes. It handles auto-advance and paste:
import { PinInput } from "@ninna-ui/forms";
import { Button, Heading, Text } from "@ninna-ui/primitives";
import { VStack } from "@ninna-ui/layout";
<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>3. Password reset with error states
Use the error and invalid props on Field for validation errors:
<Field label="Confirm password" htmlFor="confirm" error="Passwords do not match" invalid>
<Input id="confirm" type="password" />
</Field>