Search Components...

Floating Label Input

A textarea input that always shows the label and floats when the input has a value.

Loading...

Installation

Copy and paste the following code into your project.

import * as React from "react";
import { cn } from "@/lib/utils";
import { Textarea } from "@/components/ui/textarea";
import { FloatingLabel } from "./base/floating-label";
 
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
 
const FloatingTextArea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => {
	return <Textarea placeholder=" " className={cn("peer bg-transparent border-none", className)} ref={ref} {...props} />;
});
FloatingTextArea.displayName = "FloatingTextArea";
 
export type FloatingLabelTextAreaProps = TextareaProps & {
	label: string;
	error?: boolean;
};
const FloatingLabelTextArea = React.forwardRef<React.ElementRef<typeof FloatingTextArea>, React.PropsWithoutRef<FloatingLabelTextAreaProps>>(
	({ id, label, error = false, className, ...props }, ref) => {
		return (
			<div className={cn("relative", error && "[&>*]:text-error [&>fieldset]:border-error [&>fieldset]:dark:border-error")}>
				<FloatingTextArea
					ref={ref}
					id={id}
					className={cn(className, "focus-visible:ring-ring focus-visible:ring-0 focus-visible:ring-opacity-0 py-4")}
					{...props}
				/>
				<FloatingLabel
					htmlFor={id}
					className={cn("peer-focus:text-primary peer-placeholder-shown:-translate-y-0 peer-placeholder-shown:top-4", {
						"text-error peer-focus:text-error": error,
					})}>
					{label}
				</FloatingLabel>
				<fieldset
					className={cn(
						"absolute peer-focus-visible:border-2 transition-all peer-focus-visible:border-primary inset-0 -top-[5px] border dark:border-input/35 border-input/65 rounded-md m-0 py-0 text-left px-2 pointer-events-none min-w-0 peer-focus-visible:[&>legend]:max-w-full peer-placeholder-shown:[&>legend]:max-w-0",
					)}>
					<legend className="transition-all invisible whitespace-nowrap overflow-hidden w-auto max-w-full h-3 leading-4 text-xs font-normal p-0">
						<span className="px-1 visible inline-block opacity-0">{label}</span>
					</legend>
				</fieldset>
			</div>
		);
	},
);
 
FloatingLabelTextArea.displayName = "FloatingLabelTextArea";
 
export { FloatingLabelTextArea };

Base label for floating-label-textarea components.

import * as React from "react";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
 
// ----------------------------------------------------------------------
 
const floatingLabelVariant = cva(
	"cursor-text text-muted-foreground absolute start-2 z-10 transform duration-300 peer-placeholder-shown:start-2 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 font-medium peer-placeholder-shown:translate-x-1.5 leading-4 text-xs -translate-y-4 top-2 translate-x-1.5 peer-focus:top-2 peer-focus:text-xs peer-focus:-translate-y-4",
	{
		variants: {
			size: {
				sm: "peer-placeholder-shown:text-sm",
				md: "peer-placeholder-shown:text-base",
				lg: "peer-placeholder-shown:text-lg",
			},
		},
		defaultVariants: {
			size: "sm",
		},
	},
);
 
interface FloatingLabelProps extends React.ComponentPropsWithoutRef<typeof Label>, VariantProps<typeof floatingLabelVariant> {}
 
const FloatingLabel = React.forwardRef<React.ElementRef<typeof Label>, FloatingLabelProps>(({ size = "md", className, ...props }, ref) => {
	return <Label className={cn("select-none pointer-events-none", floatingLabelVariant({ size, className }))} ref={ref} {...props} />;
});
FloatingLabel.displayName = "FloatingLabel";
 
export { FloatingLabel, floatingLabelVariant, type FloatingLabelProps };

Shadcn

This component is built on top of the excellent foundation provided by Shadcn/UI.

For a deeper dive into the core concepts and building blocks, check out the original Shadcn textarea component.