How to Create a Contact Form in HTML
This recipe builds a contact form with name and email inputs plus a resizable message textarea, including a live character counter and layout that adapts from a single column to a two-column row on wider screens.
What Goes Into a Contact Form
A contact form is the simplest of the three, but the textarea makes it worth its own recipe: it needs sizing rules, a sensible character limit, and — because messages are usually the whole point of the form — a way to let people see how much room they have left as they type.
- Name — type="text" with autocomplete="name"
- Email — type="email", so replies have somewhere to go
- Message — a <textarea>, not an <input>, since it needs to hold multiple lines
- A submit button that's clearly labeled with the action, e.g. "Send Message" rather than just "Submit"
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contact Us</title>
<style>
* { box-sizing: border-box; }
body {
font-family: system-ui, sans-serif;
background: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
padding: 1rem;
}
.contact-card {
background: #fff;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.08);
width: 100%;
max-width: 480px;
}
.contact-card h1 {
font-size: 1.5rem;
margin: 0 0 1.5rem;
text-align: center;
}
.field {
margin-bottom: 1.25rem;
}
.field label {
display: block;
font-size: 0.875rem;
font-weight: 600;
margin-bottom: 0.375rem;
}
.field input,
.field textarea {
width: 100%;
padding: 0.625rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
}
.field textarea {
resize: vertical;
min-height: 120px;
}
.char-count {
font-size: 0.75rem;
color: #6b7280;
text-align: right;
margin-top: 0.25rem;
}
.submit-btn {
width: 100%;
padding: 0.75rem;
background: #2563eb;
color: #fff;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
</style>
</head>
<body>
<form class="contact-card" action="/contact" method="post">
<h1>Get in Touch</h1>
<div class="field">
<label for="contact-name">Name</label>
<input type="text" id="contact-name" name="name" autocomplete="name" required>
</div>
<div class="field">
<label for="contact-email">Email</label>
<input type="email" id="contact-email" name="email" autocomplete="email" required>
</div>
<div class="field">
<label for="contact-message">Message</label>
<textarea id="contact-message" name="message" rows="5" maxlength="500" required></textarea>
<div class="char-count"><span id="count">0</span>/500</div>
</div>
<button type="submit" class="submit-btn">Send Message</button>
</form>
<script>
const message = document.getElementById('contact-message');
const count = document.getElementById('count');
message.addEventListener('input', () => {
count.textContent = message.value.length;
});
</script>
</body>
</html>The textarea uses rows="5" to set a starting height and resize: vertical in CSS so people can drag it taller if they're writing something long, but can't drag it wider and break the card's layout. maxlength="500" caps the length the browser will accept, which pairs with the live counter so nobody hits the limit by surprise.
Making the Layout Responsive
On a narrow screen, every field should stack in one column so nothing gets cramped. On a wider screen, it's common to let the name and email fields sit side by side in a row, since they're both short single-line inputs, while the message textarea still spans the full width underneath.
Example
<!DOCTYPE html>
<html>
<head>
<style>
.contact-card {
display: block;
}
.field {
margin-bottom: 1.25rem;
}
/* Stack fields on narrow screens, but let name + email
sit side by side once there is enough room. */
@media (min-width: 480px) {
.contact-card .name-email-row {
display: flex;
gap: 1rem;
}
.contact-card .name-email-row .field {
flex: 1;
}
}
.field textarea:focus,
.field input:focus {
outline: 2px solid #2563eb;
outline-offset: 1px;
border-color: #2563eb;
}
.field textarea:invalid:not(:placeholder-shown) {
border-color: #dc2626;
}
</style>
</head>
<body>
<form class="contact-card">
<div class="name-email-row">
<div class="field">
<label for="contact-name">Name</label>
<input type="text" id="contact-name" name="name" required>
</div>
<div class="field">
<label for="contact-email">Email</label>
<input type="email" id="contact-email" name="email" required>
</div>
</div>
<div class="field">
<label for="contact-message">Message</label>
<textarea id="contact-message" name="message" rows="5" required></textarea>
</div>
</form>
</body>
</html>Once the layout and validation feel right, the contact form pairs naturally with the login and signup recipes: the same card styling, spacing, and focus states can be reused across all three so the whole site's forms feel like one consistent system.
Exercise: Forms
What does the required attribute on an input enable without any JavaScript?
Frequently Asked Questions
- How do I create a contact form in HTML?
- Use a
<form>with name and email inputs and a<textarea>for the message. Addrequiredto the fields you need, and give every control a<label>so the form is usable with a screen reader. - How do I make a contact form actually send email?
- HTML alone cannot send mail. Point the form's
actionat a backend endpoint or a form service, which receives the submission and sends the message. Themailto:action exists but opens the visitor's mail client and is unreliable.