HextaUI

Textarea

最小行数と最大行数の間で、書いた内容に合わせて滑らかに伸びる複数行テキストです。カウンター、バリデーション、送信ショートカットを備えています。

Grows with your text, then scrolls.

import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { Textarea } from "@/components/ui/textarea"

export function TextareaDemo() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Feedback</FieldLabel>
      <Textarea placeholder="What could we do better? Keep typing — the box grows with you." />
      <FieldDescription>Grows with your text, then scrolls.</FieldDescription>
    </Field>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/textarea.json

コンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。

import { Textarea } from "@/components/ui/textarea"
<Textarea placeholder="Write something…" />

ボックスはテキストに合わせて伸び、ジャンプせず新しい行になめらかに変化し、minRows から maxRows まで伸びた後はスクロールします。アニメーションが入力を遅らせることはありません。

ショートカットで送信

submitOnShortcut は、⌘ + Enter または Ctrl + Enter でフォームを送信します。Enter だけでは引き続き改行されます。

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { Textarea } from "@/components/ui/textarea"

export function TextareaSubmit() {
  const [comments, setComments] = React.useState<string[]>([])

  return (
    <form
      className="flex w-full max-w-sm flex-col gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        const form = event.currentTarget
        const text = String(new FormData(form).get("comment") ?? "").trim()
        if (text) {
          setComments((list) => [...list, text])
          form.reset()
        }
      }}
    >
      {comments.length > 0 ? (
        <ul className="flex flex-col gap-1 text-sm">
          {comments.map((comment, index) => (
            <li
              key={index}
              className="rounded-md bg-muted px-3 py-2 whitespace-pre-wrap"
            >
              {comment}
            </li>
          ))}
        </ul>
      ) : null}
      <Textarea
        name="comment"
        aria-label="Comment"
        placeholder="Write a comment…"
        minRows={2}
        submitOnShortcut
      />
      <div className="flex items-center justify-between gap-3">
        <span className="flex items-center gap-1.5 text-xs text-muted-foreground">
          <KbdGroup>
            <Kbd keys="Mod+Enter" />
          </KbdGroup>
          to send
        </span>
        <Button type="submit" size="sm">
          Comment
        </Button>
      </div>
    </form>
  )
}

文字数

Field の内側で maxLength を指定すると、FieldCounter が入力に合わせて数え、上限に達すると揺れて知らせます。

import { Field, FieldCounter, FieldLabel } from "@/components/ui/field"
import { Textarea } from "@/components/ui/textarea"

export function TextareaCounter() {
  return (
    <Field className="w-full max-w-sm">
      <div className="flex items-baseline justify-between gap-3">
        <FieldLabel>Bio</FieldLabel>
        <FieldCounter />
      </div>
      <Textarea name="bio" maxLength={160} minRows={2} />
    </Field>
  )
}

行数

minRows と maxRows は、伸び始めと止まる位置を設定します。

import { Textarea } from "@/components/ui/textarea"

export function TextareaRows() {
  return (
    <div className="grid w-full max-w-md gap-4 sm:grid-cols-2">
      <Textarea
        aria-label="One to four rows"
        minRows={1}
        maxRows={4}
        placeholder="1–4 rows"
      />
      <Textarea
        aria-label="Five to fifteen rows"
        minRows={5}
        maxRows={15}
        placeholder="5–15 rows"
      />
    </div>
  )
}

固定サイズ

autoResize={false} は、縦のリサイズハンドルを備えた固定の高さを保ちます。

import { Textarea } from "@/components/ui/textarea"

export function TextareaFixed() {
  return (
    <Textarea
      aria-label="Notes"
      autoResize={false}
      minRows={4}
      placeholder="Fixed height. Drag the corner to resize."
      className="max-w-sm"
    />
  )
}

状態

無効、読み取り専用、無効(invalid)。無効な textarea は、フォーム送信が失敗すると揺れます。

import { Textarea } from "@/components/ui/textarea"

export function TextareaStates() {
  return (
    <div className="grid w-full max-w-md gap-4 sm:grid-cols-3">
      <Textarea
        aria-label="Disabled"
        disabled
        placeholder="Disabled"
        minRows={2}
      />
      <Textarea
        aria-label="Read-only"
        readOnly
        defaultValue="Read-only text you can select but not change."
        minRows={2}
      />
      <Textarea
        aria-label="Invalid"
        aria-invalid
        placeholder="Invalid"
        minRows={2}
      />
    </div>
  )
}

右から左

テキスト、プレースホルダー、キャレットは文字の読み方向に従います。

import { Textarea } from "@/components/ui/textarea"

export function TextareaRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <Textarea aria-label="رسالتك" placeholder="اكتب رسالتك هنا…" />
    </div>
  )
}
キーアクション
Enter改行します。
⌘EntersubmitOnShortcut でフォームを送信します。Windows と Linux では Ctrl + Enter です。
  • FieldLabel または aria-label でラベルを付けます。
  • タッチスクリーンではテキストが最低16pxになるため、フォーカスしても iOS がズームしません。
  • ショートカットは、入力メソッドの変換中は待機するため、日本語や中国語の単語を確定してもメッセージが送信されることはありません。
  • 視差効果の軽減が有効な場合、ボックスは即座にリサイズされます。
プロパティ型デフォルト
autoResize固定の高さの代わりに、テキストに合わせて伸びます。
booleantrue
minRows
number3
maxRows
number10
submitOnShortcut⌘/Ctrl + Enter で親のフォームを送信します。
booleanfalse
shakeフォーム送信で無効と判定されたときに揺れます。
booleantrue
属性説明
data-slot="textarea"textarea。
data-autoresizeテキストに合わせて伸びるときに付きます。
--textarea-min-rows / --textarea-max-rowsminRows と maxRows から設定されます。

使用しているブロック

Textarea の上に構築されるブロック。