Forms

Textarea

A multi-line text input with autoresize, icons, variants, colors, sizes, and loading states.

Playground

Experiment with different props in real-time.

Basic Usage

<script lang="ts">
  import { Textarea } from 'sv5ui';
</script>

<Textarea placeholder="Write something..." />

Variants

5 visual styles.

<Textarea variant="outline" placeholder="Outline" />
<Textarea variant="none" placeholder="None" />
<Textarea variant="soft" placeholder="Soft" />
<Textarea variant="subtle" placeholder="Subtle" />
<Textarea variant="ghost" placeholder="Ghost" />

Sizes

<Textarea size="xs" placeholder="Extra Small" />
<Textarea size="sm" placeholder="Small" />
<Textarea size="md" placeholder="Medium" />
<Textarea size="lg" placeholder="Large" />
<Textarea size="xl" placeholder="Extra Large" />

With Icons

Add leading/trailing icons.

<!-- Leading icon -->
<Textarea leadingIcon="lucide:message-square" placeholder="Your message..." />

<!-- Trailing icon -->
<Textarea trailingIcon="lucide:send" placeholder="Type to send..." />

<!-- Both icons -->
<Textarea leadingIcon="lucide:edit-3" trailingIcon="lucide:check" placeholder="Edit note..." />

Leading and Trailing Slots

For more than an icon, pass leadingSlot or trailingSlot. The textarea leaves room for the slot and lets the pointer reach it, so a button in one works. Slot content cannot be clicked while the textarea is disabled or loading.

Sent 0 replies

<script lang="ts">
  import { Textarea, Button } from 'sv5ui';

  let reply = $state('');

  function send() {
    // ...post it, then clear
    reply = '';
  }
</script>

<Textarea bind:value={reply} placeholder="Write a reply...">
  {#snippet trailingSlot()}
    <Button
      icon="lucide:send"
      size="xs"
      variant="ghost"
      aria-label="Send"
      onclick={send}
    />
  {/snippet}
</Textarea>

<!-- Disabled: the slot is still shown, but it no longer takes clicks -->
<Textarea disabled value="Replies are closed">
  {#snippet trailingSlot()}
    <Button
      icon="lucide:send"
      size="xs"
      variant="ghost"
      aria-label="Send"
      onclick={send}
    />
  {/snippet}
</Textarea>

Autoresize

Auto-adjust height based on content. Use maxrows to limit.

<!-- Auto-grows as you type -->
<Textarea autoresize placeholder="Start typing and I'll grow..." />

<!-- Auto-grows with a max row limit -->
<Textarea autoresize maxrows={6} placeholder="I'll grow up to 6 rows..." />

Custom Rows

Set initial visible rows.

<Textarea rows={1} placeholder="1 row" />
<Textarea rows={5} placeholder="5 rows" />
<Textarea rows={8} placeholder="8 rows" />

Highlight

<Textarea highlight placeholder="Highlighted textarea" />

Loading & Disabled

loading shows a spinner and disables the textarea. The spinner sits on the leading side, or on the trailing side with trailing, and takes the place of an icon or a slot there.

<Textarea loading placeholder="Loading..." />

UI Slots

Use the ui prop to override classes on internal elements.

SlotDescription
rootOutermost wrapper
baseTextarea element
leadingLeading content wrapper
leadingIconLeading icon
trailingTrailing content wrapper
trailingIconTrailing icon
<Textarea
  placeholder="Custom styled"
  leadingIcon="lucide:pencil"
  ui={{
    base: 'rounded-xl shadow-lg',
    leading: 'pl-4',
    leadingIcon: 'text-primary'
  }}
/>

Snippets

SnippetDescription
leadingSlotCustom content before the text. The textarea reserves room for it and a control inside it can be clicked. Replaced by the spinner while loading
trailingSlotCustom content after the text. Same rules as leadingSlot; with trailing set, this is the side the spinner takes

Props

PropTypeDefault
valuestring-
variant'outline' | 'soft' | 'subtle' | 'ghost' | 'none''outline'
colorColorType'primary'
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'
rowsnumber3
autoresizebooleanfalse
maxrowsnumber0
highlightbooleanfalse
loadingbooleanfalse
iconstring-
leadingIconstring-
trailingIconstring-
trailingbooleanfalse
disabledbooleanfalse
placeholderstring-
refHTMLTextAreaElement | null-
classstring-
uiRecord<Slot, Class>-