Floating Label Input
An input component with a label that floats above the field when focused or filled. Uses a compound component pattern (FloatingLabel, FloatingLabel.Input, FloatingLabel.Label) for full styling control. Inspired by shadcn/ui Expansions .
Installation
npx shadcn@latest add https://prismaui.com/components/floating-label-input.json
Import
import { FloatingLabel } from '@/components/ui/floating-label-input';Preview
<FloatingLabel>
<FloatingLabel.Input id='email' type='email' />
<FloatingLabel.Label htmlFor='email'>Email</FloatingLabel.Label>
</FloatingLabel>Multiple Fields
<div className='flex flex-col gap-4'>
<FloatingLabel>
<FloatingLabel.Input id='name' />
<FloatingLabel.Label htmlFor='name'>Full Name</FloatingLabel.Label>
</FloatingLabel>
<FloatingLabel>
<FloatingLabel.Input id='email' type='email' />
<FloatingLabel.Label htmlFor='email'>Email</FloatingLabel.Label>
</FloatingLabel>
<FloatingLabel>
<FloatingLabel.Input id='password' type='password' />
<FloatingLabel.Label htmlFor='password'>Password</FloatingLabel.Label>
</FloatingLabel>
</div>Custom Styling
Each sub-component accepts its own className, giving full control without extra wrapper props.
<FloatingLabel>
<FloatingLabel.Input
id='username'
className='rounded-full border-primary/50 px-5 focus:border-primary'
/>
<FloatingLabel.Label htmlFor='username' className='left-3 text-primary'>
Username
</FloatingLabel.Label>
</FloatingLabel>With Field (Description)
Wrap FloatingLabel inside a shadcn Field to add descriptions, errors, and accessible form layouts.
We’ll never share your email.
import { Field, FieldDescription } from '@/components/ui/field';<Field>
<FloatingLabel>
<FloatingLabel.Input id='email' type='email' />
<FloatingLabel.Label htmlFor='email'>Email</FloatingLabel.Label>
</FloatingLabel>
<FieldDescription>We'll never share your email.</FieldDescription>
</Field>With Field (Error)
Use data-invalid on Field and aria-invalid on the input to show validation errors.
import { Field, FieldError } from '@/components/ui/field';<Field data-invalid>
<FloatingLabel>
<FloatingLabel.Input id='email' type='email' aria-invalid />
<FloatingLabel.Label htmlFor='email'>Email</FloatingLabel.Label>
</FloatingLabel>
<FieldError>Enter a valid email address.</FieldError>
</Field>With Field (Validation)
A complete example with interactive validation — the error appears when the input loses focus and the value is too short.
Must be at least 3 characters.
'use client';
import { useState } from 'react';
import { FloatingLabel } from '@/components/ui/floating-label-input';
import { Field, FieldDescription, FieldError } from '@/components/ui/field';
export function FieldValidationDemo() {
const [value, setValue] = useState('');
const [touched, setTouched] = useState(false);
const isInvalid = touched && value.length < 3;
return (
<Field data-invalid={isInvalid || undefined}>
<FloatingLabel>
<FloatingLabel.Input
id='username'
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={() => setTouched(true)}
aria-invalid={isInvalid || undefined}
/>
<FloatingLabel.Label htmlFor='username'>Username</FloatingLabel.Label>
</FloatingLabel>
<FieldDescription>Must be at least 3 characters.</FieldDescription>
{isInvalid && (
<FieldError>Username must be at least 3 characters.</FieldError>
)}
</Field>
);
}With FieldSet (Form)
Group multiple floating label inputs inside a FieldSet with FieldGroup for a complete form layout.
import {
Field,
FieldDescription,
FieldGroup,
FieldSet,
FieldLegend,
} from '@/components/ui/field';<FieldSet>
<FieldLegend>Account Information</FieldLegend>
<FieldDescription>Fill in your account details below.</FieldDescription>
<FieldGroup>
<Field>
<FloatingLabel>
<FloatingLabel.Input id='name' />
<FloatingLabel.Label htmlFor='name'>Full Name</FloatingLabel.Label>
</FloatingLabel>
</Field>
<Field>
<FloatingLabel>
<FloatingLabel.Input id='email' type='email' />
<FloatingLabel.Label htmlFor='email'>Email</FloatingLabel.Label>
</FloatingLabel>
<FieldDescription>
We'll send a confirmation to this address.
</FieldDescription>
</Field>
<Field>
<FloatingLabel>
<FloatingLabel.Input id='password' type='password' />
<FloatingLabel.Label htmlFor='password'>Password</FloatingLabel.Label>
</FloatingLabel>
<FieldDescription>Must be at least 8 characters.</FieldDescription>
</Field>
</FieldGroup>
</FieldSet>Props
FloatingLabel (root)
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Must contain .Input and .Label. |
className | string | — | Additional CSS classes for the root wrapper. |
FloatingLabel.Input
| Prop | Type | Default | Description |
|---|---|---|---|
...props | InputHTMLAttributes | — | All standard input props. |
className | string | — | Additional CSS classes. |
FloatingLabel.Label
| Prop | Type | Default | Description |
|---|---|---|---|
htmlFor | string | — | Matches the input id. |
children | React.ReactNode | — | The label text. |
className | string | — | Additional CSS classes. |