Multiline text controls with native values, constraints, and availability state.
app/components/nitro_kit/textarea.rbNitroKit::Textarea.new(id:, name:, value:, rows:, wrap:)Textarea values remain text content while native sizing and length constraints stay explicit.
test/dummy/app/components/gallery/components/textarea_page.rbsample("Default", slug: "default") do
render NitroKit::Textarea.new(
id: "gallery-textarea-default",
aria: { label: "Profile bio" },
name: "profile[bio]",
value: "Building reliable interfaces for analytical software.",
rows: 4
)
end
sample("Empty", slug: "empty") do
render NitroKit::Textarea.new(
id: "gallery-textarea-empty",
aria: { label: "Invitation message" },
name: "invitation[message]",
value: "",
placeholder: "Add an optional welcome message"
)
end
sample("Constrained", slug: "constrained") do
render NitroKit::Textarea.new(
id: "gallery-textarea-constrained",
aria: { label: "Incident summary" },
name: "incident[summary]",
value: "Customer impact was limited to delayed notifications.",
required: true,
rows: 6,
cols: 48,
minlength: 20,
maxlength: 280,
wrap: :hard
)
endRead-only, disabled, long, and invalid content preserve browser and accessibility semantics.
test/dummy/app/components/gallery/components/textarea_page.rbsample("Read only", slug: "readonly") do
render NitroKit::Textarea.new(
id: "gallery-textarea-readonly",
aria: { label: "Audit summary" },
name: "audit[summary]",
value: "Export completed by Ada Lovelace on July 13, 2026.",
readonly: true
)
end
sample("Disabled", slug: "disabled") do
render NitroKit::Textarea.new(
id: "gallery-textarea-disabled",
aria: { label: "Legacy notes" },
name: "legacy[notes]",
value: "Legacy notes cannot be edited.",
disabled: true
)
end
sample("Long content", slug: "long") do
render NitroKit::Textarea.new(
id: "gallery-textarea-long",
aria: { label: "Retention policy" },
name: "workspace[retention_policy]",
value: "Workspace records remain available throughout the current billing period. " \
"Audit exports preserve their original timestamps and actor identifiers across plan changes.",
rows: 8,
wrap: :soft
)
endtest/dummy/app/components/gallery/components/textarea_page.rbinvalid_profile = Gallery::FormExamples.profile(:invalid)
render NitroKit::Field.new(
nil,
:bio,
as: :textarea,
id: "gallery-textarea-invalid",
name: "profile[bio]",
value: invalid_profile.bio,
label: "Biography",
description: "Keep the biography under 280 characters.",
errors: invalid_profile.errors.full_messages_for(:bio),
required: true,
control_html: { maxlength: 280 }
)Model values and validation errors flow through FormBuilder into the direct textarea component.
test/dummy/app/components/gallery/components/textarea_page.rbprofile = Gallery::FormExamples.profile(:invalid)
form_with(
model: profile,
scope: :profile,
url: "#profile-biography",
builder: NitroKit::FormBuilder,
id: "gallery-textarea-profile-form"
) do |form|
form.field(
:bio,
as: :textarea,
id: "gallery-textarea-profile-bio",
description: "A short biography shown to workspace members.",
required: true,
control_html: { maxlength: 280 }
)
form.submit("Save biography", id: "gallery-textarea-profile-save")
end