Quick Answer

An HTML form collects information from a user and sends it to a server. You wrap your fields in a form element, add inputs, a select box, and a textarea, and connect each one to a label so it is clear and accessible. Built-in attributes like required and type="email" catch common mistakes before the form is sent, and you choose GET or POST depending on what the form does.

What Is an HTML Form?

Almost every website needs to collect information from people: a login, a search box, a sign-up, a feedback message. In HTML, all of that is handled by forms. A form is a container that groups input fields together and sends whatever the user typed off to a server.

This html forms tutorial walks through the pieces one at a time and ends with a complete, working contact form you can copy and try yourself. The good news: you do not need any JavaScript to follow along. Plain HTML can already collect input and run basic checks on it.

If HTML is still new to you, it helps to work through the basics first in our free HTML course, then come back here to put forms into practice.

The Form Element

Every form starts with the <form> element. Two of its attributes matter most:

  • action — the URL that receives the data when the form is submitted.
  • method — how the data is sent, either get or post (more on this below).
<form action="/contact" method="post">
  <!-- fields go here -->
  <button type="submit">Send</button>
</form>

When the user clicks a submit button, the browser gathers every field that has a name attribute, packs the values together, and sends them to the action URL. That last point is a common gotcha: a field without a name is never sent, even if the user typed something into it.

The <button type="submit"> is what actually triggers submission. You can also use <input type="submit">, but a real button element is easier to style and read.

Inputs and Labels

The <input> element is the workhorse of forms. A single tag becomes a text box, a checkbox, a date picker, or a dozen other controls, depending on its type:

  • type="text" — a single line of plain text.
  • type="email" — text plus a check that it looks like an email address.
  • type="password" — hides the characters as dots.
  • type="number" — numbers only, with optional min and max.
  • type="tel" — a phone number; shows a numeric keypad on phones.
  • type="date" — a built-in date picker.
  • type="checkbox" and type="radio" — on/off and pick-one choices.

Every input should have a <label>. A label is the text that tells the user what a field is for, and it must be linked to the input so screen readers and taps work correctly. You link them by matching the label's for attribute to the input's id:

<label for="email">Email address</label>
<input type="email" id="email" name="email">

Now clicking the label focuses the input — a bigger tap target that matters a lot on mobile. Skipping labels is one of the most common beginner mistakes, and it quietly breaks accessibility.

Placeholder Is Not a Label

The placeholder attribute shows faint hint text inside a field before the user types:

<input type="text" id="city" name="city" placeholder="e.g. Pune">

Placeholders are handy for showing an example, but they are not a replacement for a label. The hint disappears the moment someone starts typing, so if you rely on it alone, users forget what the field was for, and some screen readers skip it entirely. Use a real label for the name of the field, and a placeholder only to show an example of the expected format.

Built-in Validation With required and More

Browsers can check many things for you before a form is ever sent. This is called built-in (or client-side) validation, and most of it needs zero JavaScript.

The required attribute

Add required to any field the user must fill in. If it is empty, the browser blocks submission and shows a message:

<input type="text" id="name" name="name" required>

Type-based checks

Some input types validate the format automatically. type="email" checks for an @ sign and a domain-like structure, type="url" checks for a web address, and type="number" rejects letters.

Length and range

  • minlength and maxlength — limit how many characters a text field accepts.
  • min and max — set the smallest and largest allowed number or date.

Custom patterns

The pattern attribute checks a value against a regular expression. For example, a 6-digit Indian PIN code:

<input type="text" name="pincode" pattern="[0-9]{6}"
       title="Enter a 6-digit PIN code" required>

Important gotcha: built-in validation is for convenience and a smoother experience, not for security. A determined user can bypass it easily. Always validate the data again on the server before you trust or store it.

Dropdowns and Multi-line Text

Not everything fits in a one-line input. Two more elements cover the common cases.

Dropdowns with select

The <select> element creates a dropdown menu. Each choice is an <option>, and the value is what actually gets sent:

<label for="topic">Topic</label>
<select id="topic" name="topic">
  <option value="general">General question</option>
  <option value="courses">Courses</option>
  <option value="feedback">Feedback</option>
</select>

The text between the tags is what the user sees; the value is what the server receives. If you leave out value, the visible text is sent instead.

Multi-line text with textarea

For longer input like a message, use <textarea>. Note that it has no value attribute — any starting text goes between the opening and closing tags, and it needs both:

<label for="message">Message</label>
<textarea id="message" name="message" rows="5"></textarea>

The rows attribute sets the starting height in lines.

GET vs POST, Explained Simply

The method attribute decides how the browser sends the data. The two choices behave very differently.

QuestionGETPOST
Data visible in the URL?YesNo
Can be bookmarked or shared?YesNo
Good for private data like passwords?NoYes
Good for changing data on the server?NoYes
Good for a search box?YesPartial

With GET, the values are added to the end of the URL, like ?name=Asha&topic=courses. That is perfect for searches and filters you might want to bookmark, but wrong for anything private or anything that changes data.

With POST, the values travel in the body of the request, hidden from the URL. Recommendation: use POST for contact forms, sign-ups, and logins; use GET for searches and filters.

Build a Working Contact Form

Now put it all together. Here is a complete contact form that uses labels, validation, a dropdown, and a text area. Paste it into an .html file and open it in any browser.

<form action="/contact" method="post">
  <label for="name">Full name</label>
  <input type="text" id="name" name="name"
         minlength="2" required>

  <label for="email">Email address</label>
  <input type="email" id="email" name="email"
         placeholder="you@example.com" required>

  <label for="topic">Topic</label>
  <select id="topic" name="topic">
    <option value="general">General question</option>
    <option value="courses">Courses</option>
    <option value="feedback">Feedback</option>
  </select>

  <label for="message">Message</label>
  <textarea id="message" name="message"
            rows="5" required></textarea>

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

Try submitting it with an empty name or a bad email address — the browser stops you and points to the problem, all without a single line of JavaScript. Because the method is post, the values are sent privately in the request body.

The action="/contact" URL is where a real server would receive and process the data. Building that server side is the natural next step, and you can learn a common approach in our PHP course or with Node.js.

A Quick Checklist for Every Form

Forms get easy once the pattern clicks. Keep this short checklist in mind each time you build one:

  • Wrap everything in a <form> with a sensible action and method.
  • Give every field a name, or its value will not be sent.
  • Pair every input with a linked <label> for accessibility and bigger tap targets.
  • Use placeholder for examples only, never as the label.
  • Add required and the right input type to get free validation.
  • Choose POST for private or data-changing forms, and GET for searches.
  • Always validate again on the server — client-side checks can be bypassed.

Get those seven habits right and your forms will be clear, accessible, and reliable.

Frequently Asked Questions

What is the difference between an input's id and name?

The id links a field to its <label> and must be unique on the page. The name is the key the server sees when the form is submitted. A field needs a name to be sent at all, and an id to be paired with a label, so most inputs use both.

Do I need JavaScript to validate an HTML form?

No. Attributes like required, type="email", minlength, and pattern let the browser catch common mistakes with no JavaScript at all. You add JavaScript only for custom rules or nicer error messages. Either way, always validate again on the server, because client-side checks can be bypassed.

Should I use GET or POST for a contact form?

Use POST. A contact form sends a message and often personal details, so the data should not appear in the URL or browser history. Save GET for things like search boxes and filters that are safe to bookmark and share.

Why does clicking my label not focus the input?

The label's for value must match the input's id exactly, and IDs are case-sensitive. If they differ, or the input has no id, the link breaks. Alternatively, you can wrap the input inside the <label> tags, which links them without needing matching attributes.

Can a placeholder replace a label?

No. A placeholder disappears as soon as the user starts typing and is not reliably announced by screen readers, so using it as the only label hurts usability and accessibility. Keep a visible label for the field's name and use the placeholder only to show an example format.