▶ Free previewModuleForms That Collect Real Data

Build a contact form with labels, text inputs and a submit button

A WhatsApp link is perfect for a customer who's ready to chat. Plenty aren't. They want to send the details once, get on with their day and hear back later. And a business owner answering fifty enquiries a week wants every one of them to arrive with the same details, in the same order, instead of "hi, how much?"

That's what a form is for. This module builds one properly: here the structure, then choices, the right keyboards, validation, and finally a real inbox.

Words in this lesson

WordWhat it means, in plain English
<form>The container holding the fields and the submit button.
<label>The visible name of a field, tied to it so tapping the words focuses the box.
nameThe attribute that decides what each answer is called when it's sent.
<textarea>A multi-line text box.
Submit buttonThe control that sends the form.
FocusWhich field the keyboard is currently typing into.

The <form> element

<form method="post">
  <!-- controls go here -->
</form>

<form> wraps every control whose data should be sent together. Both tags are mandatory, and one form can't contain another form.

Two attributes decide what happens when the form is sent.

action is the URL that processes the submission: where the data goes. Your form doesn't have anywhere to send to yet; you'll add the action in Lesson 5.5, when you connect a real service. Until then, pressing the button won't deliver the message anywhere, and that's expected.

method decides how the data is sent. The two you need:

MethodWhat happens to the data
get (the default)Added to the end of the action URL, after a ?
postSent in the body of the request, not in the URL

MDN's guidance for choosing is one line: use GET when the form has no side effects. A search box is GET: searching changes nothing, and a URL containing the search can be bookmarked or shared.

A contact form is the opposite. Submitting it does something: it sends a message and creates an enquiry. So a contact form uses method="post".

There's a second reason to keep contact forms off GET. With GET, everything the visitor typed — name, phone number, message — goes into the address bar, where it can be copied, shared along with the link, or read over someone's shoulder. A phone number doesn't belong in a URL.

What actually gets sent

When a form is submitted, the browser collects name–value pairs: each control's name attribute, paired with what the visitor entered. Pairs are joined with &.

With GET you could see them. A form with fields named name and phone would produce a URL ending something like:

?name=Chidera&phone=08031234567

POST sends the same pairs, just not in the URL.

The consequence is the most important rule of this module: a control without a name sends nothing. It will display, the visitor can type into it, and when the form is sent its contents quietly vanish. Formspree, the service you'll connect in Lesson 5.5, says the same about its own forms: any element with a name attribute is sent.

Labels

Every control needs a visible label, linked to it:

<label for="name">Your name</label>
<input type="text" id="name" name="name">

The link works through two attributes that must match: the label's for and the input's id.

That link is more than tidiness. MDN describes two concrete benefits:

  • For screen reader users, the label is programmatically associated with the field, so when they move into the field, the screen reader reads the label out. Without it they hear "edit text" and have to guess what to type.
  • For everybody, clicking or tapping the label moves focus to its field. That's a bigger target, which matters a lot on a phone. Tap the word "Your name" and the keyboard opens for that box.

Two ways to associate a label

Explicit, with for and id, as above.

Implicit, by wrapping the control inside the label:

<label>
  Your name
  <input type="text" name="name">
</label>

Both work. MDN generally recommends the explicit for form for compatibility with external tools and assistive technologies, so that's what this course uses.

Remember id and name do different jobs. The id connects the label, and must be unique on the page. The name labels the data when it's sent. They're often the same word, which is fine, but they're not the same thing.

Single-line text: <input>

<label for="name">Your name</label>
<input type="text" id="name" name="name">

<input> is a void element. There's no closing tag and no </input>, the same as <img> and <br>. type="text" is the default single-line box. In Lesson 5.3 you'll swap it for types that bring up better phone keyboards.

Multi-line text: <textarea>

For the message itself, the visitor needs room:

<label for="message">Your message</label>
<textarea id="message" name="message" rows="5" cols="33"></textarea>

<textarea> behaves differently from <input> in two ways that catch people out:

It isn't a void element. Both the opening and closing tags are mandatory.

Its starting content goes between the tags, not in a value attribute. Whatever sits between <textarea> and </textarea> is the text the box starts with. That includes spaces and line breaks, so if you indent a closing tag onto its own line, the box starts full of blank space. Keep the two tags together with nothing between them.

rows sets how many lines are visible (the default is 2) and cols sets the width in average character widths (the default is 20). MDN recommends setting both, because browser defaults can differ.

The button

<button type="submit">Send enquiry</button>

Inside a form, a <button> with no type is a submit button by default. The three types:

typeWhat it does
submitSends the form. The default inside a form
buttonNothing on its own; it needs JavaScript
resetClears every field back to its starting value

Always write type explicitly. MDN's warning is that a button you didn't mean to submit will try to anyway. Writing type="submit" makes your intention obvious to the next person reading the code.

Don't add a reset button. MDN notes, dryly, that this behaviour "tends to annoy users". One mis-tap wipes a message someone spent five minutes writing.

You'll also see <input type="submit">. It works, but <button> can contain other markup and is much easier to style, so this course uses <button>.

And make the button's text say what happens. "Send enquiry" tells the visitor exactly what they're doing. "Submit" is the form equivalent of "click here".

A complete first form

<h2 id="enquiry">Send an Enquiry</h2>

<form method="post">
  <!-- the action is added in Lesson 5.5 -->

  <label for="name">Your name</label>
  <input type="text" id="name" name="name">

  <label for="phone">Phone or WhatsApp number</label>
  <input type="text" id="phone" name="phone">

  <label for="message">Your message</label>
  <textarea id="message" name="message" rows="5" cols="33"></textarea>

  <button type="submit">Send enquiry</button>
</form>

Check each control against three questions: does it have a label, linked with a matching for and id? Does it have a name? Is the id unique on the page?

It will look cramped: labels and boxes jammed together on a line. That's the browser's default styling, and laying forms out neatly is CSS, which comes in the next course. Get the structure right now.

Common mistakes

MistakeConsequence
A control with no nameIts contents are never sent
Label for doesn't match the input's idNo association: no read-out, no bigger tap target
The same id used twiceLabels attach to the wrong field
method="get" on a contact formPersonal details end up in the URL
Whitespace between <textarea> tagsThe box starts full of blank space
<textarea value="...">Ignored; starting text goes between the tags
A form inside another formNot allowed
A reset buttonOne mis-tap wipes the whole message
Button text "Submit"Says nothing about what will happen

The short version

  • A WhatsApp link suits someone ready to chat now. A form suits everyone else — and it suits the owner answering enquiries at the end of the day.
  • Every field needs a <label>. It's what screen readers announce, and it makes the tap target much bigger on a phone.
  • A field with no name is not sent. That's the commonest reason an answer goes missing.
  • Use <input> for one line, <textarea> for a message.
  • The button's job is to submit — say what it does (Send enquiry), not just "Submit".

Try it yourself

Build a working structure on the page that holds your contact details:

  1. Add a heading and a <form method="post"> with a comment noting that the action comes in Lesson 5.5.
  2. Add a name field, a phone field and a message <textarea>, each with a linked <label>, a unique id and a name.
  3. Set rows and cols on the textarea, and keep its two tags together.
  4. Add a <button type="submit"> whose text says what it does.
  5. Click every label in turn and confirm the right field receives focus.
  6. Inspect the form in DevTools and check the for of each label against the id of its field.
  7. Remove the name from the phone field. Nothing on screen changes, which is the point: that field would now send nothing. Put it back.
  8. If you use Live Server, temporarily change the method to get, fill the form in and press the button. Read the address bar: you'll see the field names and your text after a ?. Change it back to post, and note in a comment why a contact form shouldn't use GET.

Next lesson adds dropdowns, radio buttons and checkboxes.

You'll know you've got it when tapping a field's label focuses the box, and you check for a name on every field without being told.