Displays a form textarea or a component that looks like a textarea.
Installation
npx shadcn@latest add @caprice/textareanpm install @caprice-ui/reactInstall the following dependencies:
npm install @base-ui/react lucide-reactAdd a cn helper
import { defineConfig, type VariantProps } from 'cva';import { twMerge } from 'tailwind-merge';export const { compose, cva, cx: cn,} = defineConfig({ hooks: { onComplete: (className: string) => twMerge(className), },});export type { VariantProps };Copy and paste the following code into your project.
'use client';import { mergeProps, useRender } from '@base-ui/react';import { type PressEvents, useFocusRing, usePress } from 'react-aria';import { cva, type VariantProps } from '@/lib/utils';const textareaVariants = cva({ base: [ 'field-sizing-content flex min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs outline-none placeholder:text-muted-foreground', // Transition 'transition-[color,background-color,border-color,box-shadow]', // Focus state, using focus instead of focus-visible to avoid confusion with data-focus-visible 'focus:border-ring focus:ring-[3px] focus:ring-ring/50 focus:placeholder:text-current', // Real "focus-visible", only visible when interacting with a keyboard. // ref: https://react-spectrum.adobe.com/react-aria/useFocusRing.html 'data-focus-visible:outline-2 data-focus-visible:outline-primary data-focus-visible:outline-solid data-focus-visible:outline-offset-2 data-focus-visible:ring-transparent dark:data-focus-visible:outline-primary/64', // Disabled state 'disabled:cursor-not-allowed disabled:opacity-50', // Invalid state 'aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:data-focus-visible:outline-destructive/80 dark:aria-invalid:data-focus-visible:outline-destructive/48', ], variants: { variant: { outline: [ 'bg-transparent', // Hover state 'not-disabled:hover:not-focus-within:bg-input/32 not-disabled:hover:not-focus-within:placeholder:text-current dark:not-disabled:hover:not-focus-within:bg-input/72', // Pressed state 'data-pressed:bg-input/48 dark:data-pressed:bg-input/84', ], }, }, defaultVariants: { variant: 'outline', },});/** * A multi-line text input. * Renders a `<textarea>` element. * * Documentation: [Caprice UI Textarea](https://caprice-ui.com/docs/components/textarea) */export namespace Textarea { export type Variants = { /** * Variant * @default 'outline' */ variant?: VariantProps<typeof textareaVariants>['variant']; }; export type State = { /** * Whether the textarea is focused using a keyboard */ isFocusVisible: boolean; /** * Whether the textarea is being pressed */ isPressed: boolean; }; export type Props = useRender.ComponentProps<'textarea', Textarea.State> & PressEvents & Textarea.Variants;}export function Textarea({ className, variant, render, // PressEvents onPress, onPressStart, onPressChange, onPressEnd, onPressUp, onClick, ...props}: Textarea.Props) { const { isFocusVisible, focusProps } = useFocusRing({ isTextInput: true }); const { pressProps, isPressed } = usePress({ onPress, onPressStart, onPressChange, onPressEnd, onPressUp, onClick, isDisabled: props.disabled, }); const defaultProps: useRender.ElementProps<'textarea'> & { 'data-slot'?: string } = { 'data-slot': 'textarea', className: textareaVariants({ variant, className }), }; return useRender({ defaultTagName: 'textarea', render, state: { isFocusVisible, isPressed, }, stateAttributesMapping: { isFocusVisible: (value) => (value ? { 'data-focus-visible': '' } : null), isPressed: (value) => (value ? { 'data-pressed': '' } : null), }, props: mergeProps<'textarea'>(defaultProps, pressProps, focusProps, props), });}Update the import paths to match your project setup.
Usage
import { Textarea } from "@/components/caprice-ui/textarea"import { Textarea } from "@caprice-ui/react/textarea"<Textarea />Examples
Default
Disabled
Invalid
With Label
With Text
With Button
API Reference
State
Prop
Type
Props
Prop
Type