Float Label
float-label ui Floating label wrapper for any input element. The label floats up when the input is focused or has a value. Wrap it around any input, select, or textarea. Supports required indicator and disabled state.
Also available for Vue ->Installation
$ pnpm dlx shadcn@latest add https://uipkge.dev/r/react/float-label.json $ npx shadcn@latest add https://uipkge.dev/r/react/float-label.json $ yarn dlx shadcn@latest add https://uipkge.dev/r/react/float-label.json $ bunx shadcn@latest add https://uipkge.dev/r/react/float-label.json Named registry:
npx shadcn@latest add @uipkge-react/float-label Installs to: components/ui/float-label/ Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
label | string | — | required |
required | boolean | false | optional |
disabled | boolean | false | optional |
Files installed (2)
-
components/ui/float-label/FloatLabel.tsx 3.2 kB
'use client' import * as React from 'react' import { cn } from '@/lib/utils' export interface FloatLabelProps extends React.HTMLAttributes<HTMLDivElement> { label: string required?: boolean disabled?: boolean } const FloatLabel = React.forwardRef<HTMLDivElement, FloatLabelProps>( ({ label, required = false, disabled = false, className, children, ...props }, ref) => { const wrapperRef = React.useRef<HTMLDivElement | null>(null) const [isFocused, setIsFocused] = React.useState(false) const [hasValue, setHasValue] = React.useState(false) const isFloating = isFocused || hasValue const setRefs = React.useCallback( (node: HTMLDivElement | null) => { wrapperRef.current = node if (typeof ref === 'function') ref(node) else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node }, [ref], ) function checkValue(target: EventTarget | null) { const el = target as HTMLElement | null if (!el) return if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) { setHasValue(!!el.value) } else { const input = el.querySelector?.('input, textarea, select') as | HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null if (input) setHasValue(!!input.value) } } // Check for prefilled value on mount (before any focus/input events fire). React.useLayoutEffect(() => { const wrapper = wrapperRef.current if (!wrapper) return const input = wrapper.querySelector('input, textarea, select') as | HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null if (input) setHasValue(!!input.value) }, []) function handleFocusIn(event: React.FocusEvent<HTMLDivElement>) { setIsFocused(true) checkValue(event.target) } function handleFocusOut(event: React.FocusEvent<HTMLDivElement>) { setIsFocused(false) checkValue(event.target) } function handleInput(event: React.FormEvent<HTMLDivElement>) { checkValue(event.target) } const wrapperClasses = cn( 'relative flex flex-col', disabled && 'opacity-50 cursor-not-allowed', className, ) const labelClasses = cn( 'text-muted-foreground pointer-events-none absolute left-3 z-10 bg-transparent px-1 text-sm transition-all duration-200', !isFloating && 'top-1/2 -translate-y-1/2', isFloating && 'top-0 -translate-y-1/2 scale-75 bg-background text-foreground', isFocused && 'text-ring', required && "after:text-destructive after:ml-0.5 after:content-['*']", ) return ( <div ref={setRefs} className={wrapperClasses} data-uipkge="" data-slot="float-label" data-floating={isFloating} onFocus={handleFocusIn} onBlur={handleFocusOut} onInput={handleInput} onChange={handleInput} {...props} > <label className={labelClasses}>{label}</label> {children} </div> ) }, ) FloatLabel.displayName = 'FloatLabel' export { FloatLabel } -
components/ui/float-label/index.ts 0.1 kB
export { FloatLabel, type FloatLabelProps } from './FloatLabel'
Raw manifest: https://uipkge.dev/r/react/float-label.json