Skip to content
Drivn
2 min read

React Form Components — Input, Select, Checkbox, Button

Form UI for React — Input, Textarea, Select, Combobox, Checkbox, Radio, Switch, Slider. Drivn ships each with label, error, and ref forwarding built in.

Forms are where users decide whether your product respects their time. A mislabeled field, an error message that appears three lines away from the input it describes, or a submit button that gives no feedback all read as carelessness — and carelessness costs conversions. Drivn's form family inverts the usual default: label pairing, inline error display, and ref forwarding are built in, so accessibility is what you get unless you deliberately remove it. Every control binds to React Hook Form through a plain register() call, or to useState with value and onChange, with no wrapper component or context provider to mount at the app root.

This hub links every input-family component with a one-line description and a rule of thumb for when to reach for it. The controls share one API surface — label, error, helperText, disabled, size, plus the native props of the element underneath — so the second component you learn costs almost nothing after the first. Async validation, server-returned errors, and submit-in-flight state all fold into that same shape, which is what keeps a growing form codebase from fragmenting into a pile of one-off patterns nobody wants to maintain.

Text entry

Use input for single-line values (email, name, URL). Use textarea for free-form text with auto-resize and a character count. Both support icons on either side, an error state that renders a red ring and message below, and a ref forward that plays nicely with React Hook Form's register().

For short, constrained entries (country, currency, priority), use select — it renders a native-feeling dropdown with keyboard support. For long lists with search, use combobox, which adds filtering, multi-select, and icon support.

Choice and toggle

For a single choice among 2-4 options, use a radio-group. For multiple independent choices, use checkbox. For a boolean on/off, use switch — visually stronger than a checkbox and more appropriate for immediate-effect toggles (dark mode, notifications on/off).

Use toggle when the control is an action ("Bold", "Italic") rather than a data state; it has a pressed visual that reads as a button rather than a form field.

Numeric ranges

When a value lives on a continuous scale, reach for a slider rather than a text input. It supports step snapping, vertical orientation, and the same size variants as the rest of the family, and it lets the user feel the range — volume, opacity, playback position, zoom — in a way a number field never will. Save the input for exact figures or ranges too wide to drag through comfortably.

Pair every slider with a live value label so the current setting is always readable, and match the step to the range: 1 across 0-100, 0.1 across 0-10. Dragging is awkward on touch screens and invisible to keyboard-only users, so expose a paired numeric field or a clear aria-valuetext for anyone who cannot use the thumb. Treated this way, the slider stays a fast, tactile control without becoming an accessibility dead end.

Submit and validation

Every form ends in a button. Drivn's Button ships with a loading prop that handles async submission — it sets aria-busy, disables clicks, and preserves the button width. Combine it with the React Hook Form integration to bind loading state to formState.isSubmitting.

For validation messages, set the error prop on each field — do not rely on browser default tooltips, which users ignore. Consistent in-field error styling builds trust and reduces retry rate.

1'use client'
2import { useForm } from 'react-hook-form'
3import { Input } from '@/components/ui/input'
4import { Button } from '@/components/ui/button'
5
6export function SignupForm() {
7 const { register, handleSubmit, formState } = useForm<{ email: string }>()
8 return (
9 <form onSubmit={handleSubmit(console.log)}>
10 <Input
11 label="Email"
12 error={formState.errors.email?.message}
13 {...register('email', { required: 'Required' })}
14 />
15 <Button loading={formState.isSubmitting}>Sign up</Button>
16 </form>
17 )
18}
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →