Textarea
A form control for entering multiple lines of text.
Installation
Included in poetry-ui — available as
poetry_textarea
the moment you've installed Poetry,
with no per-component step. To own the source and edit it, copy it into your app:
bin/rails g poetry:add textarea
Default
<%# Standalone textareas carry an aria-label; inside a form, pair with
Field instead (it wires label, ids, and aria). %>
<div class="w-80">
<%= poetry_textarea(name: "message", placeholder: "Type your message here.",
"aria-label": "Message") %>
</div>
Auto grown
<%# Auto-grow is the field-sizing-content CSS property (Chromium) - no
JS autosizer. Elsewhere: min-h-16 plus the native resize handle. %>
<div class="w-80">
<%= poetry_textarea(name: "notes", "aria-label": "Notes",
value: "Paragraph one.\n\nParagraph two keeps pushing the height - no JS autosizer, the CSS does it.\n\nParagraph three.") %>
</div>
Button
<%# Upstream's textarea-button: a bare textarea and its action in one
grid - the button stretches to the full field width (grid children
fill the column; no justify-end wrapper). %>
<div class="grid w-full max-w-sm gap-2">
<%= poetry_textarea(name: "message", placeholder: "Type your message here.") %>
<%= poetry_button { "Send message" } %>
</div>
Disabled
<%= poetry_textarea(name: "message", placeholder: "Type your message here.",
disabled: true, "aria-label": "Message", class: "w-80") %>
Field
Enter your message below.
<%# Upstream's textarea-field demo: label, description, then the control -
hint_position: :above keeps the guidance visible before a tall
textarea; aria-describedby is wired through control_attributes
either way. %>
<div class="w-full max-w-sm">
<%= poetry_field(id: "textarea-field-message", label_text: "Message",
hint: "Enter your message below.", hint_position: :above) do |field| %>
<%= poetry_textarea(name: "message", placeholder: "Type your message here.",
**field.control_attributes.transform_keys(&:to_sym)) %>
<% end %>
</div>
Invalid
<%# Error styling comes from aria-invalid - in real forms the
Field/FormBuilder sets it from model errors, never by hand. %>
<div class="w-80">
<%= poetry_textarea(name: "message", placeholder: "Type your message here.",
value: "too short", invalid: true, "aria-label": "Message") %>
</div>
With hint
A sentence or two shown on your public profile.
<%# Field carries a hint below the control - its id lands in the
textarea's aria-describedby, so the guidance is announced, never a
bare <p>. Wire everything through field.control_attributes. %>
<div class="w-80">
<%= poetry_field(id: "textarea-bio", label_text: "About you",
hint: "A sentence or two shown on your public profile.") do |field| %>
<%= poetry_textarea(name: "bio", rows: 4,
placeholder: "What should people know about you?",
**field.control_attributes.transform_keys(&:to_sym)) %>
<% end %>
</div>
With label
<%# Field owns the ids and yields control_attributes - wire the textarea
with them, never hand-write the label/aria pairing. %>
<div class="w-80">
<%= poetry_field(id: "textarea-message", label_text: "Your message") do |field| %>
<%= poetry_textarea(name: "message", placeholder: "Type your message here.",
**field.control_attributes.transform_keys(&:to_sym)) %>
<% end %>
</div>
API
Poetry::Ui::Textarea::Component — options are
constructor keywords (the poetry_* helper forwards them);
slots are composed inside the block. Generated from the gem's source documentation.
| Option | Type | Details | Description |
|---|---|---|---|
| disabled: | Boolean | defaults to false |
Disables the control and forwards to the native element. |
| invalid: | Boolean | defaults to false |
Marks the field errored (aria-invalid + the destructive ring); set by Field/FormBuilder from model errors. |
| name: | String | The submitted field name. | |
| placeholder: | String | Hint text shown while empty - never a substitute for a label. | |
| rows: | Integer | The initial visual rows - the minimum height under CSS auto-grow, and the fixed size in browsers without it. | |
| value: | String | The initial text, rendered as the element's content. |
Styling
Every part carries a stable data-slot attribute — target
[data-slot=…] from your own CSS to restyle it. State rides
data attributes on the parts below. This contract is verified against rendered DOM in CI.
| Part | Description |
|---|---|
| [data-slot=textarea] | The <textarea> element itself - value renders as content; auto-grow is the field-sizing-content CSS property, zero JS |