ComponentsForms
Form
Styled form wrapper component for consistent form layouts and structure
The Form component is a lightweight wrapper around the native HTML <form> element. It provides consistent styling, spacing, and structure for building forms. It extends all standard form attributes, so you can attach onSubmit, action, method, and any other native form props.
Import
import { Form } from '@nim-ui/components';Usage
Code
<Form onSubmit={handleSubmit}>
<FormField label="Name" name="name">
<Input placeholder="Operations lead" />
</FormField>
<FormField label="Email" name="email">
<Input type="email" placeholder="ops@example.com" />
</FormField>
<Button type="submit" variant="primary">
Submit
</Button>
</Form>Form Layouts
Vertical Layout (Default)
Code
<Form onSubmit={handleSubmit}>
<div className="grid gap-4 sm:grid-cols-2">
<FormField label="First name" name="firstName">
<Input placeholder="Nim" />
</FormField>
<FormField label="Last name" name="lastName">
<Input placeholder="Team" />
</FormField>
</div>
<FormField label="Email" name="email">
<Input type="email" placeholder="ops@example.com" />
</FormField>
<FormField label="Message" name="message">
<Textarea placeholder="Share the operational context..." rows={4} />
</FormField>
<Button type="submit" variant="primary">
Send Message
</Button>
</Form>Inline Fields
Code
<Form onSubmit={handleSubmit}>
<div className="grid gap-4 sm:grid-cols-2">
<FormField label="First name" name="firstName">
<Input placeholder="Nim" />
</FormField>
<FormField label="Last name" name="lastName">
<Input placeholder="Team" />
</FormField>
</div>
<FormField label="Email" name="email">
<Input type="email" placeholder="ops@example.com" />
</FormField>
<Button type="submit" variant="primary">
Create Account
</Button>
</Form>With Select and Checkbox
Code
<Form onSubmit={handleSubmit}>
<FormField label="Full name" name="name">
<Input placeholder="Nim Operations" />
</FormField>
<FormField label="Work email" name="email">
<Input type="email" placeholder="ops@example.com" />
</FormField>
<div className="space-y-2">
<label htmlFor="plan" className="block text-sm font-medium">
Plan
</label>
<Select>
<SelectTrigger id="plan" className="w-full">
<SelectValue placeholder="Select plan" />
</SelectTrigger>
<SelectContent>
<SelectItem value="personal">Personal</SelectItem>
<SelectItem value="business">Business</SelectItem>
<SelectItem value="enterprise">Enterprise</SelectItem>
</SelectContent>
</Select>
</div>
<Checkbox id="terms" label="I agree to the terms and conditions" />
<Button type="submit" variant="primary" fullWidth>
Sign Up
</Button>
</Form>With Validation Errors
Code
<Form onSubmit={handleSubmit}>
<FormField label="Email" name="email" error={errors.email}>
<Input type="email" placeholder="ops@example.com" />
</FormField>
<FormField label="Password" name="password" error={errors.password}>
<Input type="password" placeholder="Password" />
</FormField>
<Button type="submit" variant="primary" fullWidth>
Log In
</Button>
</Form>Props
The Form component extends React.FormHTMLAttributes<HTMLFormElement>, so it accepts all standard HTML form attributes in addition to the following:
| Name | Type | Default | Description |
|---|---|---|---|
onSubmit | (event: React.FormEvent<HTMLFormElement>) => void | - | Submit event handler. Call event.preventDefault() to handle submission in JavaScript. |
action | string | - | URL to submit the form to when using native form submission |
method | 'get' | 'post' | - | HTTP method for native form submission |
className | string | - | Additional CSS classes to apply to the form element |
children* | ReactNode | - | Form content including inputs, selects, buttons, and other form elements |
Usage Examples
Login Form
Operator console
Sign in
Use your workspace email to continue.
Code
import { useState } from 'react';
import { Form, FormField, Input, Button, Checkbox } from '@nim-ui/components';
function LoginForm() {
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
const formData = new FormData(e.currentTarget as HTMLFormElement);
const email = formData.get('email') as string;
const password = formData.get('password') as string;
try {
await login(email, password);
} catch (err) {
setErrors({ email: 'Invalid email or password' });
} finally {
setLoading(false);
}
};
return (
<div className="w-full max-w-sm rounded-md border border-neutral-200 bg-white p-6 shadow-soft">
<div className="mb-5">
<p className="text-xs font-medium uppercase text-neutral-500">Operator console</p>
<h3 className="mt-1 text-xl font-semibold text-neutral-950">
Sign in
</h3>
<p className="mt-1 text-sm text-neutral-600">Use your workspace email to continue.</p>
</div>
<Form onSubmit={handleSubmit} className="gap-5">
<FormField label="Email" name="email" error={errors.email}>
<Input
name="email"
type="email"
placeholder="ops@example.com"
autoComplete="email"
/>
</FormField>
<FormField label="Password" name="password" error={errors.password}>
<Input
name="password"
type="password"
placeholder="Password"
autoComplete="current-password"
/>
</FormField>
<div className="flex flex-wrap items-center justify-between gap-3">
<Checkbox id="remember" label="Keep me signed in" name="remember" />
<a href="#" className="text-sm font-medium text-primary-700">
Forgot password?
</a>
</div>
<Button type="submit" variant="primary" fullWidth loading={loading}>
Sign in
</Button>
</Form>
</div>
);
}Contact Form
import {
Form,
FormField,
Input,
Textarea,
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
Button,
} from '@nim-ui/components';
function ContactForm() {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget as HTMLFormElement);
await sendContactMessage(Object.fromEntries(formData));
};
return (
<Form onSubmit={handleSubmit} className="gap-5">
<div className="grid gap-4 sm:grid-cols-2">
<FormField label="First name" name="firstName">
<Input name="firstName" placeholder="Nim" />
</FormField>
<FormField label="Last name" name="lastName">
<Input name="lastName" placeholder="Team" />
</FormField>
</div>
<FormField label="Email" name="email">
<Input name="email" type="email" placeholder="ops@example.com" />
</FormField>
<div className="space-y-2">
<label htmlFor="subject" className="block text-sm font-medium">
Subject
</label>
<Select name="subject">
<SelectTrigger id="subject" className="w-full">
<SelectValue placeholder="What is this about?" />
</SelectTrigger>
<SelectContent>
<SelectItem value="general">General Inquiry</SelectItem>
<SelectItem value="support">Technical Support</SelectItem>
<SelectItem value="billing">Billing</SelectItem>
<SelectItem value="feedback">Feedback</SelectItem>
</SelectContent>
</Select>
</div>
<FormField label="Message" name="message">
<Textarea name="message" placeholder="Your message..." rows={6} />
</FormField>
<Button type="submit" variant="primary">
Send Message
</Button>
</Form>
);
}Multi-step Form
import { useState } from 'react';
import { Form, FormField, Input, Button } from '@nim-ui/components';
function MultiStepForm() {
const [step, setStep] = useState(1);
return (
<Form onSubmit={handleFinalSubmit} className="gap-5">
{step === 1 && (
<>
<FormField label="Full name" name="name">
<Input name="name" placeholder="Nim Operations" />
</FormField>
<FormField label="Email" name="email">
<Input name="email" type="email" placeholder="ops@example.com" />
</FormField>
<Button variant="primary" onClick={() => setStep(2)}>
Next
</Button>
</>
)}
{step === 2 && (
<>
<FormField label="Company" name="company">
<Input name="company" placeholder="Nim UI" />
</FormField>
<FormField label="Role" name="role">
<Input name="role" placeholder="Operations lead" />
</FormField>
<div className="flex gap-4">
<Button variant="outline" onClick={() => setStep(1)}>
Back
</Button>
<Button type="submit" variant="primary">
Submit
</Button>
</div>
</>
)}
</Form>
);
}Accessibility
The Form component follows accessibility best practices:
- Uses the semantic
<form>HTML element - Supports native form validation attributes on child inputs
- Works with screen readers by maintaining proper form landmark semantics
- All child form elements should include associated labels (see FormField)
- Error messages are announced to screen readers via
aria-liveregions