What Are Tailwind Forms?
The @tailwindcss/forms plugin gives native inputs, selects, checkboxes and radios one consistent baseline and leaves the visual design to utility classes. Tailwind Labs maintains it, npm distributes it, and it ships under the MIT license.
Browser-specific styling is the reason it exists. Padding, rounded corners or a custom checkbox color often do nothing until appearance: none is applied. That gap is a cross-browser compatibility problem, and the plugin closes it at the base layer.
It builds on the utility-first model of Tailwind CSS and brings no components of its own. Text-like inputs, textarea, select, checkbox and radio all get the same baseline, and each one then accepts padding, border, rounded and color utilities directly. Prebuilt components, labels and layouts are missing, and so are error messages, validation logic and form state. The plugin also has no opinion on brand colors, spacing or typography.
The Tailwind Labs README describes it as a form reset, not a set of component styles. That framing explains most of how it behaves.
Which Form Elements Does the Plugin Reset?
The reset covers 17 selectors (Tailwind Labs README). Of those, 12 are text-like input types: text, password, email, number, url, date, datetime-local, month, week, time, search and tel. The rest are checkbox, radio, select, select[multiple] and textarea.
Each element gets the same minimal baseline of border and padding, with a focus ring on top. Select also gets a chevron drawn as a background image, and checkbox and radio add a checked fill that follows the text color.
Since release 0.5.4, an input with no type attribute gets the text input styles too (project changelog).
File and range inputs are not on the list, so they keep native styling.
As of October 2026, the latest release is 0.5.11 (December 17, 2025), which limits the plugin’s attribute rules to input and select elements, according to the project changelog.
How to Install the Forms Plugin in Tailwind v4 and v3
Run npm install -D @tailwindcss/forms, then register the plugin with an @plugin line in your stylesheet (Tailwind v4) or in the plugins array of tailwind.config.js (Tailwind v3).
- Install the package from npm as a dev dependency.
- Register it for your Tailwind version (details below).
- Run the dev server or build step (Vite, PostCSS or the Tailwind CLI) so the stylesheet that holds the directive gets processed.
- Add a bare select and a checkbox to a page. If the native browser styling is gone, the plugin is loaded.
Tailwind has to be running first, so set up the framework itself before adding the plugin.
Tailwind v4 setup
Add two lines to the main stylesheet:
@import "tailwindcss";
@plugin "@tailwindcss/forms";
Tailwind’s documentation files @plugin under the compatibility directives for loading JavaScript plugins, and it accepts a package name or a local path.
If nothing changes after an upgrade, check that @plugin follows @import and that the build processes that CSS file. The UAE Design System documentation uses this exact pattern in its v4 starter stylesheet, next to the typography plugin.
Tailwind v3 setup
One config entry does it.
plugins: [require('@tailwindcss/forms')]
Put that inside tailwind.config.js.
The reset rides on the base layer, so a stylesheet without the @tailwind base directive gets none of it. In v3, that directive injects Tailwind’s base styles plus any base styles registered by plugins (Tailwind v3 documentation).
Base or Class Strategy: Which One Fits Your Project?
For a new project, the default is the right call. Class makes sense when the plugin has to share a page with existing form CSS or a third-party widget, and base only fits if no form-* class will ever be needed.
| Strategy | Global styles | form-* classes | Best fit |
|---|
| Default (no option) | Yes | Yes | New projects |
| base | Yes | No | New projects, global reset only |
| class | No | Yes | Existing CSS, custom select or date picker libraries |
The class strategy exists for custom widgets. According to the changelog, it was added for custom select and date picker libraries.
Old tutorials disagree with the current default, which changed in release 0.5.0 (March 2, 2022). Before that the default was base only, and 0.5.0 made it generate both global styles and classes.
Under the class strategy, form-input handles the 12 text-like input types and form-textarea handles textarea. form-select styles select, form-multiselect styles select[multiple], and form-checkbox and form-radio cover the last two.
Because the classes are generated as well, a div can look like an input by carrying form-input. That is the use case the README gives for them.
In Tailwind v4 the option goes inside the plugin block: @plugin "@tailwindcss/forms" { strategy: "class"; }. In v3 it is passed as an argument instead: require("@tailwindcss/forms")({ strategy: 'class' }).
The catch with class is that a bare input keeps its native look until form-input is added, because no global reset runs.
How to Customize Inputs, Selects, Checkboxes and Radios
Utilities apply directly to the reset controls. Padding, border, rounded and focus utilities work on inputs and selects, and a text color utility sets the checked fill on checkboxes and radios.
Text inputs and textarea
Text-like inputs and textarea take the same utilities as any other element. For a wider look at how Tailwind styles input fields, that page covers the general patterns.
Common choices are px-4 py-3 for spacing, rounded-lg or rounded-full for shape, and border-gray-300 for the border. For focus, focus:border-indigo-500 focus:ring-indigo-500 is the usual pair.
Under the class strategy, add form-input or form-textarea first, then the utilities: form-input rounded-full px-4 py-3 (Tailwind Labs README).
Select and multiselect
Padding finally works on a select.
The README example is rounded-full px-4 py-3 on a bare select, which used to need custom CSS.
The chevron is part of the baseline. Release 0.5.4 (July 13, 2023) removed it from selects with a non-default size, so a select given a size attribute renders without one.
Under the class strategy a single select takes form-select and a multiple select takes form-multiselect. With base, the multiple attribute does the job.
Checkboxes and radio buttons
Set the checked color with a text color utility. rounded text-pink-500 on a checkbox turns the checked fill pink (Tailwind Labs README).
Radios follow the same rule, and there is a separate walkthrough on styling radio buttons in Tailwind.
Size comes from height and width utilities such as h-5 w-5.
Since release 0.5.7 (November 10, 2023), checkboxes and radios keep their normal appearance in forced-colors mode, the mode behind Windows high contrast themes. Check the chosen fill color against its background anyway.
How to Build a Responsive Form Layout
Default to one column, move to a two-column grid from the sm breakpoint up, and let wide fields span both columns.
Treating the form as a grid layout keeps spacing consistent, because one gap utility spaces every cell and no field needs its own margin.
Tailwind follows mobile-first design, so unprefixed classes apply at every width and prefixed ones apply from their breakpoint up. A wrapper of grid grid-cols-1 gap-4 sm:grid-cols-2 gives one column on phones and two on larger screens.
| Field | Control | Width classes |
|---|
| First name | input | none (one cell) |
| Last name | input | none (one cell) |
| Email | input, type email | sm:col-span-2 |
| Message | textarea | sm:col-span-2 |
| Submit | button | sm:col-span-2 |
For label pairing, give each input an id and point the label’s for attribute at it. Wrapping the input inside the label also works, and it is the pattern Tailwind’s own state documentation uses. Add block w-full to the input so it fills its grid cell.
Helper text sits under the control in text-sm text-gray-500. Capping the form width with max-w-sm mx-auto is a quick way to stop inputs stretching across a wide screen.
Styling Focus, Invalid and Disabled States
State variants let one class list style every condition. The focus:, invalid:, required: and disabled: prefixes sit beside the base utilities and the browser decides which one applies. Tailwind’s documentation notes that this cuts the conditional logic a template needs.
Stacking focus:invalid:border-pink-500 keeps the error color on a field that has focus (Tailwind documentation example). A plain invalid style is invalid:border-pink-500 invalid:text-pink-600, and a disabled one is disabled:bg-gray-50 disabled:text-gray-500 disabled:border-gray-200.
The invalid: variant matches the moment a field fails its constraints, so an empty required input is already flagged on page load. The native :user-invalid pseudo-class waits for interaction. Web platform Baseline data lists it as newly available since November 2023 and widely available since May 2026.
Tailwind v4.1 and later include user-invalid: and user-valid: variants, so user-invalid:border-pink-500 works directly. On v3, reach it with an arbitrary variant: [&:user-invalid]:border-pink-500.
For error messages, mark the input with the peer class and hide its message with invisible peer-invalid:visible, the pattern in Tailwind’s documentation. For errors set by JavaScript, use aria-[invalid=true]:border-pink-500 together with aria-invalid and aria-describedby, as described in guides to accessible forms.
Color alone does not communicate an error. MDN warns that people who struggle to tell colors apart cannot read a red border as invalid, so pair it with text or an icon.
WCAG 2.2 success criterion 1.4.11 requires at least 3:1 contrast between the visual information needed to identify a control or its states and the adjacent colors (W3C). Light borders such as border-gray-300 on white are the common casualty when the border is what identifies the field, so measure each border and focus ring for color contrast.
Disabled fields are exempt from that rule, which makes opacity a legitimate way to dim them.
When Does the Forms Plugin Not Work?
File inputs, range inputs, Safari date and time rendering, accent-color utilities on checkboxes and the Play CDN are where the plugin lets you down.
| Case | What you see | Workaround |
|---|
| File input | Native button and label text | file: variant on the input |
| Range input | Native slider | accent-* utilities tint it |
| Date and time, Safari | Spacing and alignment glitches | Update to 0.5.6 or later |
| accent-* on checkbox or radio | No visible change | Text color sets the checked fill |
| Play CDN | Plugin styles missing unless the plugin is enabled; Tailwind’s v4 Play CDN docs describe no way to load it | v3: add ?plugins=forms to the script URL. v4: use a build step |
Date and time inputs are the most patched control type in the project. Four changelog entries (0.1.2, 0.4.1, 0.5.5 and 0.5.6) fix date or time rendering in Safari or on iOS, so test them on a real iPhone before shipping.
Tailwind’s accent-* utilities override the browser’s default color on native checkboxes and radio groups. The plugin removes that native rendering, which means text color is the lever that works.
The select chevron has its own wrinkle. Release 0.5.9 (September 5, 2024) falls back to a static chevron color when the theme uses CSS variables, so check the icon against dark backgrounds.
On the Play CDN, Tailwind’s v3 documentation shows the forms plugin loading through a ?plugins=forms query on the script URL. Tailwind’s v4 Play CDN page documents no equivalent. Both versions’ Play CDN pages call it a development tool: v4 says it is not intended for production, and v3 says it is not the best choice for production.
Forms Plugin or a Component Library?
If you own the design and only want a reset, the forms plugin is enough. A component library earns its place when you would rather get finished controls than write custom markup.
| Option | What it provides | Styling approach | Best fit |
|---|
| Forms plugin | Reset for native controls | Utilities on native elements | Custom design systems |
| Flowbite | Prebuilt inputs, selects, file upload, toggles | Tailwind classes in component markup | Ready-made forms |
| daisyUI | Classes such as input, select, checkbox | Semantic names plus modifiers | Short markup, theming |
| Headless UI | Unstyled accessible form components | You write every style | Accessible React forms |
| Tailwind Plus | Copy-paste form layouts | Prebuilt markup, one-time license | Existing license holders only (closed to new customers since September 9, 2026) |
A UI component library adds a dependency and its own markup conventions, which is the price of finished controls.
Two of the three official Tailwind Labs pieces still stack rather than compete. The plugin resets native controls, and Headless UI adds behavior and aria wiring. The third, Tailwind Plus, supplied styled layouts but is no longer sold to new customers.
The forms plugin itself has nothing to learn or uninstall, though no labels, errors or layouts come with it.
Flowbite has sizes, disabled inputs and floating labels ready to go, and its current docs target Tailwind v4 while saying it also works with v3.
daisyUI customizes components with Tailwind utilities, and its guidance is to append ! to a utility if specificity blocks an override. Version 5 requires Tailwind v4.
Headless UI v2.0 for React added 8 form components (Fieldset, Legend, Field, Label, Description, Input, Select, Textarea), and styling stays entirely yours.
Tailwind Plus is official, polished, copy-paste markup sold as a one-time purchase. Tailwind Labs closed sign-ups to new customers on September 9, 2026, when it announced it is joining Shopify, and existing customers keep access.
For a single login or contact form, a library is more than the job needs. The plugin plus a dozen utilities costs less to maintain.
Tailwind Forms FAQ
Is the forms plugin still needed in Tailwind v4?
Yes. The reset ships as a separate package, and the Tailwind Labs README still lists @tailwindcss/forms for v4, loaded with an @plugin line.
Without it, native controls keep their browser styling.
Is @tailwindcss/forms free to use?
Yes. The package carries the MIT license, so personal and commercial projects can use it at no cost.
Tailwind Plus form blocks were a separate paid, one-time purchase, and Tailwind Plus has been closed to new customers since September 9, 2026.
Where can I find free Tailwind form templates?
FormBold publishes an MIT-licensed GitHub repository of form templates and code examples written in plain Tailwind CSS, though it was last updated in 2022. Flowbite documents ready-made form components with copyable markup.
Check each license before reusing the markup in client work.
How do I style form inputs for dark mode?
Stack dark: variants on the same element, the way Tailwind’s documentation writes dark:disabled:border-gray-700 next to the light classes.
In v4, a class-based toggle needs a custom variant rule, written as @custom-variant dark with a selector for your dark class.
How do I build a floating label input?
Mark the input with the peer class, give it a single-space placeholder, and move the label with peer-focus and peer-placeholder-shown variants.
Tailwind’s documentation notes that peer variants make floating labels possible without any JavaScript.
Does the forms plugin work with React Hook Form and Formik?
Yes. The plugin is CSS only, so it styles the native inputs those libraries render.
Validation state comes from the library, which then drives error classes or aria-invalid on the field.
What Handles Submission and Validation Once Styling Is Done?
Tailwind forms stops at styling. Validation rules live in native HTML constraint attributes, form state in a library such as React Hook Form or Formik, and the final check on the server.
The order of those layers decides how much code a form needs. Start with the required, type and pattern attributes, bring in a form library once fields depend on each other, and validate on the server before anything is stored.
Attributes come first because invalid: and user-invalid styling read their results directly. A library adds a dependency, so it waits until field logic demands it. The server check stays last because a browser rule does not bind a direct request.
One annoyance remains. The browser draws its own native validation bubbles, and they take no utility classes, so custom error text means JavaScript-driven messages.
Verified October 2026: the plugin’s README still frames it as a reset. A move into component styling would change this split.