Textarea
A multi-line text field.
import { Div } from "@implementjs/core";
import { Label } from "@/lib/components/ui/label";
import { Textarea } from "@/lib/components/ui/textarea";
export default function TextareaDemo() {
return Div(
{ class: "flex w-full max-w-sm flex-col gap-2" },
Label({ for: "message" }, "Message"),
Textarea({ id: "message", placeholder: "Tell us what happened…", class: "max-h-40" }),
Div({ class: "text-sm text-muted-foreground" }, "It grows with what you type."),
);
}Installation
npx jsrepo add @implementjs/ui/textarea
Nothing else comes with it — this one stands alone on @implementjs/core and @implementjs/primitives.
Copy the file below to src/lib/components/ui/textarea.ts. It imports cn, so copy utils.ts to src/lib/utils.ts too.
import { Textarea as TextareaElement, type Child, type ElementProps } from "@implementjs/core";
import { createComponent } from "@implementjs/primitives";
import { cn } from "@/lib/utils";
export type TextareaProps = ElementProps<"textarea">;
/**
* A multi-line field, styled to match [Input](/ui/input). `field-sizing-content`
* grows it with what is typed where the browser supports it, between the min
* height here and whatever `max-h-*` you add.
*/
export const Textarea = createComponent(function Textarea(
{ class: className, ...props }: TextareaProps,
...children: Child[]
) {
return TextareaElement(
{
...props,
"data-slot": "textarea",
class: cn(
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none md:text-sm",
"placeholder:text-muted-foreground",
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
"disabled:cursor-not-allowed disabled:opacity-50",
"dark:bg-input/30",
className,
),
},
...children,
);
});Usage
import { Textarea } from "@/lib/components/ui/textarea";
Textarea({ id: "message", placeholder: "Tell us what happened…" });
Styled to match Input, down to the focus ring and the invalid state.
It grows with the content
field-sizing-content makes the textarea size itself to what is typed, where the browser supports it. The base class sets a minimum of min-h-16; add a max-h-* to stop it running away:
Textarea({ id: "message", class: "max-h-40" });
Where field-sizing is not supported the textarea simply keeps its minimum height and scrolls, which is the old behavior — nothing breaks.
API Reference
Textarea
A multi-line field, styled to match Input. field-sizing-content grows it with what is typed where the browser supports it — cap it with a max-h-* class. Renders a Textarea; extra props are forwarded onto it.