Select, Textarea and Buttons

Use <select>/<option>/<optgroup> for dropdown choices, <textarea> for multi-line text, and understand button type behavior well enough to avoid the classic accidental-submit bug.

Not every form control is an <input>. Three more elements round out the toolkit: <select> for picking from a predefined dropdown list, <textarea> for open-ended multi-line text, and <button> for triggering actions — submitting, resetting, or running custom script. Each has a small set of rules that are easy to overlook and that cause real, frequently-reported bugs when ignored — especially button's default type, which silently submits forms that were never meant to submit.

select, option, and optgroup

<select> renders a dropdown control. Each choice inside it is an <option>, and the option's value attribute is what actually gets submitted under the select's name — not necessarily the text the user sees. If an option has no value attribute, its own text content is used as the fallback value instead.

You can mark one option selected by default with the selected attribute. Adding the multiple attribute to <select> turns it into a multi-select list where the user can choose more than one option (typically by ctrl/cmd-clicking).

When a dropdown has many options that fall into natural categories, <optgroup label="..."> lets you visually and semantically cluster related <option> elements under a shared heading — for example, grouping countries by continent inside one long country selector. optgroup is purely organizational; it doesn't submit a value itself.

textarea for multi-line text

<textarea> is for input that can span multiple lines — comments, messages, bios, feedback. Unlike <input>, its default value is written as the element's text content rather than a value attribute: whatever appears between the opening and closing <textarea> tags is what shows up pre-filled. The rows and cols attributes hint at its initial rendered size (though CSS typically overrides this in real layouts), and most browsers let the user resize it manually via a drag handle in the corner.

The button element and its type attribute

<button> can hold rich inner content — text, icons, even nested images — unlike an <input type="submit">, which can only ever show plain text. Its behavior is governed by the type attribute, which takes one of three values.

type="submit" submits the enclosing form, exactly like input type="submit". type="reset" clears every control in the form back to its default values. type="button" does nothing on its own at all — it's inert by default and exists purely as a hook for JavaScript event handlers.

Here is the trap: when a <button> inside a <form> has no type attribute specified at all, its default type is submit. This surprises a huge number of developers who add a button meant only to trigger some JavaScript — say, a 'Show password' toggle or an 'Add another row' button in a dynamic form — and place it inside a <form> without explicitly typing it. The very first click submits the whole form early, often navigating the page away or triggering a premature save, and the bug can be hard to spot because the button 'works' in the sense that clicking it does something (just the wrong something).

The disabled attribute

Any form control — input, select, textarea, or button — can take the boolean disabled attribute. A disabled control is grayed out, cannot be focused or edited, and critically, is excluded entirely from form submission: its value never gets sent, even if it has one. This differs from the readonly attribute (available on some inputs and textarea), which prevents editing but still submits the field's value. Disabling a submit button while an async request is in flight is a common pattern for preventing duplicate submissions from an eager double-click.

select, textarea, and button type

option's value attribute (not its visible text) is what gets submitted. textarea's default content goes between the tags, not in a value attribute. Always set button's type explicitly inside a form to avoid accidental submission.

<select name="country">
  <optgroup label="North America">
    <option value="us">United States</option>
    <option value="ca">Canada</option>
  </optgroup>
</select>

<textarea name="bio" rows="4" cols="30">Default bio text</textarea>

<button type="submit">Save</button>
<button type="button">Preview</button>

Code Examples

'Pro' appears pre-selected when the page loads because it carries the selected attribute. If the form submits without the user changing anything, plan=pro is sent.

The dropdown visually clusters options under the 'Popular' and 'Systems' headings, making a long list of choices easier to scan without changing what actually gets submitted.

The placeholder-like text is written directly inside the tags as content, not as a value attribute — a common point of confusion for anyone used to input's value syntax.

The emoji button has no type attribute, so its default is submit. Clicking it to insert an emoji actually submits the entire form immediately — publishing a draft the user only meant to edit. The fix is adding type="button" to the emoji button.

Common mistake: An untyped <button> inside a <form> defaults to type="submit", not type="button".

With type="button" explicitly set, clicking the emoji button only runs insertEmoji() and never touches form submission — the form only submits when 'Publish' is clicked.

<select name="plan">
  <option value="free">Free</option>
  <option value="pro" selected>Pro</option>
  <option value="enterprise">Enterprise</option>
</select>
<select name="language">
  <optgroup label="Popular">
    <option value="js">JavaScript</option>
    <option value="py">Python</option>
  </optgroup>
  <optgroup label="Systems">
    <option value="c">C</option>
    <option value="rust">Rust</option>
  </optgroup>
</select>
<label for="comments">Comments</label>
<textarea id="comments" name="comments" rows="5">
Tell us what you think...
</textarea>
<form action="/save-draft" method="POST">
  <textarea name="content"></textarea>
  <button onclick="insertEmoji()">😀</button>
  <button type="submit">Publish</button>
</form>
<form action="/save-draft" method="POST">
  <textarea name="content"></textarea>
  <button type="button" onclick="insertEmoji()">😀</button>
  <button type="submit">Publish</button>
</form>

Important Rules

An option's value attribute (falling back to its text content if omitted) is what gets submitted, not necessarily what's displayed.

textarea's default text goes inside the tags, not in a value attribute.

Always give every <button> inside a <form> an explicit type — an untyped button defaults to submit and will trigger submission on click.

disabled controls are excluded from submission entirely; readonly controls still submit their value.

optgroup organizes options visually and semantically but has no value of its own.

Why this matters in practice

The untyped-button-inside-a-form bug is common enough to be a recurring entry on bug trackers for real production apps — a 'like' button, an icon toggle, or a step-navigation button added inside a multi-step <form> without type="button" can silently submit the form on first click, sometimes wiping out unsaved input or triggering a premature network request. Defensive teams often adopt a blanket rule: every single <button> in the codebase gets an explicit type, no exceptions.

<button onclick="toggleTheme()">Dark mode</button> (inside a <form>, no type)

<button type="button" onclick="toggleTheme()">Dark mode</button>

Without type="button", this control defaults to submit and will submit the enclosing form every time it's clicked, even though it was only meant to toggle a theme.

<textarea name="bio" value="Default text"></textarea> <textarea name="bio">Default text</textarea> textarea ignores a value attribute entirely — its default content must be placed between the opening and closing tags.

[Choose the Correct Syntax] Inside a <form>, which button is guaranteed NOT to submit the form when clicked? A. <button>Preview</button> B. <button type="submit">Preview</button> C. <button type="button">Preview</button> D. <input type="submit" value="Preview">

C. <button type="button">Preview</button> — type="button" is the only option here that does not submit the form — it's inert by default and only does something if wired up with JavaScript. The first option is a trap: an untyped <button> inside a form defaults to type="submit", so it behaves identically to the second option.

A multi-step signup form has a 'Next' button meant only to reveal the next step via JavaScript, sitting inside the <form>. Write the button so it never accidentally submits the form.

<button type="button" onclick="showNextStep()">Next</button>

The default type for a button inside a form is the one you don't want here.