Forms: The Basics
Every other element talks at your visitor. A form lets them talk back.
Think about everything you've built so far. Headings, paragraphs, images, tables. All of it is one-way: the page speaks, the visitor listens.
Forms change that. A form is a conversation. Your site asks a question ("What's your name?"), the visitor answers, and then your site responds. Login screens, search bars, checkout pages, "contact us" boxes: all conversations, all built on forms.
Let's learn to hold one.
The whole conversation: <form>
<form action="/subscribe" method="post">
<label for="email">Your email address</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>The <form> element wraps the whole exchange. Its two main attributes say what happens when the visitor finishes answering:
actionis where the answers get sent, like the address on a reply envelope.methodis how they get sent.getputs the answers right in the web address (fine for a search box, since you might want to bookmark the results).posttucks them inside the request, out of the address bar and browser history (the right choice for anything private, or anything that changes something, like signing up). It doesn't encrypt anything by itself, though: HTTPS, the padlock in the address bar, is what keeps answers safe on their way.
Where those answers actually go is a server, a program that receives the data and does something with it. That's a story for later in the book, in the backend tracks. For now, we're building the question side of the conversation.
Asking a question: <label> + <input>
Every good question has two parts: the question itself, and a place to answer.
<label>is the question: "Your email address."<input>is the blank line where the answer goes.
They're connected by matching the label's for to the input's id. That link is more important than it looks:
- Clicking the label focuses the input. Try it in the preview below. That makes tiny checkboxes and radio buttons far easier to hit, especially on phones.
- Screen readers read the label aloud when someone lands on the input. Without it, a blind visitor hears "edit text, blank." Edit text for what? Their name? Their card number? No idea.
"Can't I just use a placeholder?"
You'll see this everywhere:
<!-- Please don't -->
<input type="email" placeholder="Email address">It looks clean, so the temptation is real. But a placeholder is a hint, not a label. It vanishes the moment someone starts typing, so if they get distracted halfway through, the question is gone. It's usually pale grey text that's hard to read, and some assistive tools don't treat it as a label at all.
It's like a waiter who asks you a question, then forgets it the moment you open your mouth. Use a real <label>. Keep placeholders for small example hints, like placeholder="name@example.com".
name: the label on the answer
When the form is sent, the browser packages up each answer with its name:
email=maria@example.comWithout a name, an input's answer is simply left out of the package. The visitor typed it, the form looked fine, and the data never arrived. If you ever build a form where one field mysteriously "doesn't send," check for a missing name first.
So you've now got three similar-looking attributes. Here's the quick way to keep them straight:
idconnects the input to its label on the page.namelabels the answer when it's sent away.for(on the label) points to the input'sid.
Different questions, different inputs
The type attribute changes what kind of answer an input expects, and that changes a lot, especially on phones:
<input type="text"> <!-- anything -->
<input type="email"> <!-- phone keyboards show an @ key -->
<input type="password"> <!-- hides what's typed -->
<input type="tel"> <!-- phones show a number pad -->
<input type="number"> <!-- up/down arrows for quantities -->
<input type="url"> <!-- for web addresses -->
<input type="date"> <!-- a date picker -->
<input type="search"> <!-- a search box -->Picking the right type is like handing someone the right tool before they ask. A phone number field that pops up a number pad instead of the full keyboard? That's a small kindness your visitors will feel even if they never notice why.
Helping browsers fill it in: autocomplete
<input type="text" id="full-name" name="full-name" autocomplete="name">
<input type="email" id="email" name="email" autocomplete="email">
<input type="tel" id="phone" name="phone" autocomplete="tel">The autocomplete attribute tells the browser what kind of answer a field wants, so it can offer to fill it in from what it already knows: a name, an email address, a street address, even a saved password (autocomplete="current-password"). For people with motor or memory difficulties, not having to type the same details on every site is a real kindness, and accessibility guidelines ask for it on fields about the visitor themselves. Add it whenever a field asks for personal details.
Ending the conversation: <button>
<button type="submit">Subscribe</button>A submit button sends the form off. Always write the type, because a <button> inside a form defaults to submitting, which surprises people when they add a button meant to do something else, like "Show password."
And make the button text say what will happen. "Subscribe," "Send message," "Create account." Not just "Submit." Would you trust a real person who ended every conversation with "Submit"?
Try it
This preview is a sandbox, so pressing the button won't really send anything anywhere. Everything else works.
HTML
Preview
Try it yourself
- Click the words "Your name" in the preview. The cursor jumps into the box. That's the label doing its job.
- Change the
foron the first label towrong-idand click it again. The connection is broken. - Add a phone number question with the right
type, alabel, anid, and aname.
Check your understanding
Up next
A conversation with only text answers gets dull fast. In More Form Controls you'll add dropdowns, checkboxes, and multiple-choice questions. Labels come back again in Accessibility Basics, where you'll see just how much they matter. And if the id versus name versus for difference is still blurry, a quick look back at Attributes might help.
