Textarea
Multi-line text field sharing the input's designs and states.
Installation
Terminal
npx magic-native-ui@latest add textareaUsage
The example running in the preview above. Imports use the default aliases from components.json.
textarea-demo.tsx
import { View } from 'react-native'
import { Label } from '@/components/ui/label'
import { Text } from '@/components/ui/text'
import { Textarea } from '@/components/ui/textarea'
export function TextareaDemo() {
return (
<View className="w-full gap-5">
<View className="gap-1.5">
<Label nativeID="bio">Bio</Label>
<Textarea placeholder="Tell us about yourself" aria-labelledby="bio" />
</View>
<Textarea variant="filled" size="sm" placeholder="Filled, two lines high" />
<View className="gap-1.5">
<Textarea invalid defaultValue="Too short." size="sm" />
<Text className="text-xs text-destructive">Use at least 40 characters.</Text>
</View>
</View>
)
}Component source
What the CLI copies into your project. Once it is there, the code is yours to edit.
registry/ui/textarea.tsx
import { TextInput } from 'react-native'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/registry/lib/utils'
const textareaVariants = cva('w-full px-3 py-2 text-base text-foreground', {
variants: {
variant: {
default: 'rounded-md border border-input bg-background focus:border-ring',
filled: 'rounded-md border border-transparent bg-muted focus:border-ring',
ghost: 'rounded-md border border-transparent bg-transparent focus:bg-muted',
},
// A minimum rather than a fixed height, so `className` can still grow it.
size: {
sm: 'min-h-16',
default: 'min-h-24',
lg: 'min-h-36',
},
invalid: {
true: 'border-destructive focus:border-destructive',
false: '',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
invalid: false,
},
})
type TextareaProps = Omit<React.ComponentProps<typeof TextInput>, 'size'> &
VariantProps<typeof textareaVariants>
function Textarea({
className,
variant,
size,
invalid,
editable,
readOnly,
...props
}: TextareaProps) {
// `TextInput` has no `disabled` prop, so the `disabled:` variant never fires
// on it — the dimming is applied from the props that do turn a field off.
const off = editable === false || readOnly === true
return (
<TextInput
multiline
editable={editable}
readOnly={readOnly}
aria-invalid={invalid ?? undefined}
// Android centres multiline text vertically without this.
textAlignVertical="top"
className={cn(textareaVariants({ variant, size, invalid }), off && 'opacity-50', className)}
// Colour props are resolved from `accent-*` utilities, not `text-*`.
placeholderTextColorClassName="accent-muted-foreground"
{...props}
/>
)
}
export { Textarea, textareaVariants }
export type { TextareaProps }