Textarea

Displays a form textarea or a component that looks like a textarea.

Basic
Invalid
With Label
With Description

Type your message and press enter to send.

Disabled

Installation

Install the dependencies

pnpm add cn class-variance-authority

Copy the component

Copy the following files into src/lib/components/ui/textarea.

src/lib/components/ui/textarea/Textarea.svelte
<script lang="ts">
  import { Input as Primitive } from '@sveltery/base/input';
  import type { InputProps } from '@sveltery/base/input';
  import { cn } from 'cn';
  import type { HTMLAttributes, HTMLTextareaAttributes } from 'svelte/elements';
  import type { TextareaProps } from './types.js';
  let { class: classProp, value = $bindable(), ...props }: TextareaProps = $props();
  // A bound remote field's spread carries the input's `type`, which a textarea has no use for.
  // The server renders a spread `value` as an attribute, so `value` goes on the textarea itself.
  function withoutTypeOrValue(control: HTMLAttributes<HTMLElement>) {
    const rest: Record<string, unknown> = { ...control };
    delete rest.type;
    delete rest.value;
    return rest as HTMLTextareaAttributes;
  }
</script>
<!-- Base labels, describes and validates its control, so the textarea renders through it. -->
<Primitive {...props as InputProps} bind:value>
  {#snippet render(control)}
    <textarea data-slot="textarea" {...withoutTypeOrValue(control)} value={(control as HTMLTextareaAttributes).value} class={cn('border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors focus-visible:ring-3 aria-invalid:ring-3 md:text-sm flex field-sizing-content min-h-16 w-full outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', classProp)}></textarea>
  {/snippet}
</Primitive>
src/lib/components/ui/textarea/types.ts
import type { HTMLTextareaAttributes } from 'svelte/elements';

/** A textarea holds one value, so `value` takes what a Field control takes. */
export type TextareaProps = Omit<HTMLTextareaAttributes, 'value'> & { value?: string | number | null };
src/lib/components/ui/textarea/index.ts
export { default as Textarea } from './Textarea.svelte';
export type { TextareaProps } from './types.js';

Update the import paths

Change the imports to match your project setup.

Usage

<script lang="ts">
  import { Textarea } from '$lib/components/ui/textarea';
</script>
<Textarea name="message" placeholder="Type your message here." />