Skip to main content
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>

Related Blocks

Component Docs

FAQ