How to Create a Multi-Step Forms in HTML Without JavaScript – Multi-step forms improve user experience by breaking complex forms into manageable sections. While JavaScript is commonly used to create these interfaces, there are compelling reasons to implement them using HTML and CSS only: improved accessibility, faster loading times, and greater compatibility with older browsers. This comprehensive guide will walk you through several techniques to create effective multi-step forms without a single line of JavaScript.
Table of Contents
Why Create Multi-Step Forms Without JavaScript?
Before diving into implementation, let’s understand the benefits of JavaScript-free multi-step forms:
- Better performance: Lighter pages that load faster
- Improved accessibility: Works even when JavaScript is disabled
- Greater browser compatibility: Functions on older browsers
- Reduced dependencies: No need for external libraries
- Simplified maintenance: Less code to maintain
According to a Web Almanac performance report, JavaScript is often the most resource-intensive asset on websites, making CSS-only solutions attractive for performance optimization.
Technique 1: CSS-Only Multi-Step Form with the :target Selector
The CSS :target
selector offers an elegant way to create multi-step forms. This technique uses anchor links to navigate between form sections without JavaScript. The MDN Web Docs on :target provide more information about this powerful selector.

How It Works:
- Create separate sections for each form step
- Use anchor links to navigate between steps
- Apply the
:target
selector to show/hide appropriate sections
Step-by-Step Implementation:
First, let’s structure our HTML:
<div class="multi-step-form">
<!-- Navigation -->
<div class="form-nav">
<a href="#step1" class="active">Personal Info</a>
<a href="#step2">Contact Details</a>
<a href="#step3">Review</a>
</div>
<!-- Form -->
<form action="/submit-form" method="post">
<!-- Step 1 -->
<div id="step1" class="form-step">
<h2>Personal Information</h2>
<div class="form-group">
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="dob">Date of Birth</label>
<input type="date" id="dob" name="dob" required>
</div>
<div class="form-nav-buttons">
<a href="#step2" class="btn next">Next Step</a>
</div>
</div>
<!-- Step 2 -->
<div id="step2" class="form-step">
<h2>Contact Details</h2>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" required>
</div>
<div class="form-nav-buttons">
<a href="#step1" class="btn prev">Previous Step</a>
<a href="#step3" class="btn next">Next Step</a>
</div>
</div>
<!-- Step 3 -->
<div id="step3" class="form-step">
<h2>Review Your Information</h2>
<p>Please review all your information before submitting.</p>
<div class="form-nav-buttons">
<a href="#step2" class="btn prev">Previous Step</a>
<button type="submit" class="btn submit">Submit Form</button>
</div>
</div>
</form>
</div>
Now, let’s apply CSS to make the multi-step functionality work:
/* Basic styling */
.multi-step-form {
max-width: 600px;
margin: 0 auto;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 5px;
}
.form-nav {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
border-bottom: 1px solid #eee;
}
.form-nav a {
padding: 10px;
text-decoration: none;
color: #333;
position: relative;
}
.form-nav a.active {
font-weight: bold;
color: #4285f4;
}
/* Form steps styling */
.form-step {
display: none;
animation: fadeIn 0.5s;
}
/* Show the first step by default */
#step1 {
display: block;
}
/* Target selector makes the clicked step visible */
#step1:target,
#step2:target,
#step3:target {
display: block;
}
/* Hide other steps when one is targeted */
#step1:target ~ #step2,
#step1:target ~ #step3,
#step2:target ~ #step1,
#step2:target ~ #step3,
#step3:target ~ #step1,
#step3:target ~ #step2 {
display: none;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-nav-buttons {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.btn {
padding: 10px 15px;
background-color: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
}
.btn.prev {
background-color: #ccc;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
This implementation uses the :target
pseudo-class which activates when the element’s ID matches the URL’s fragment (the part after #). When a user clicks on a navigation link, the browser scrolls to the element with the matching ID and the CSS :target
selector shows that specific step while hiding others.
For a live demonstration of this technique, check out this CodePen example of a CSS-only multi-step form.
Technique 2: Using Radio Buttons for Multi-Step Forms
Another JavaScript-free approach uses hidden radio buttons to control form step visibility. According to the A11Y Project, this method can be more accessible for screen readers when properly implemented.

How It Works:
- Create radio buttons for each step (hidden from view)
- Style labels as navigation buttons
- Use CSS selectors to show/hide sections based on radio button state
Implementation:
<div class="multi-step-form">
<form action="/submit-form" method="post">
<!-- Radio buttons act as controllers -->
<input type="radio" name="step" id="radio-step1" checked class="step-control">
<input type="radio" name="step" id="radio-step2" class="step-control">
<input type="radio" name="step" id="radio-step3" class="step-control">
<!-- Navigation -->
<div class="progress-bar">
<label for="radio-step1" class="progress-step">Step 1</label>
<label for="radio-step2" class="progress-step">Step 2</label>
<label for="radio-step3" class="progress-step">Step 3</label>
</div>
<!-- Step 1 content -->
<div class="form-step step1">
<h2>Personal Information</h2>
<div class="form-group">
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="dob">Date of Birth</label>
<input type="date" id="dob" name="dob" required>
</div>
<div class="form-controls">
<label for="radio-step2" class="btn next">Next</label>
</div>
</div>
<!-- Step 2 content -->
<div class="form-step step2">
<h2>Contact Details</h2>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" required>
</div>
<div class="form-controls">
<label for="radio-step1" class="btn prev">Previous</label>
<label for="radio-step3" class="btn next">Next</label>
</div>
</div>
<!-- Step 3 content -->
<div class="form-step step3">
<h2>Review & Submit</h2>
<p>Please review your information before submitting.</p>
<div class="form-controls">
<label for="radio-step2" class="btn prev">Previous</label>
<button type="submit" class="btn submit">Submit Form</button>
</div>
</div>
</form>
</div>
And the CSS:
/* Hide radio controls */
.step-control {
display: none;
}
.multi-step-form {
max-width: 600px;
margin: 0 auto;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.progress-bar {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
position: relative;
}
.progress-bar::before {
content: "";
position: absolute;
top: 50%;
left: 0;
height: 2px;
width: 100%;
background-color: #ddd;
z-index: 0;
}
.progress-step {
background-color: #ddd;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 1;
cursor: pointer;
transition: background-color 0.3s;
}
/* Hide all form steps by default */
.form-step {
display: none;
}
/* Style active step in progress bar */
#radio-step1:checked ~ .progress-bar .progress-step:nth-child(1),
#radio-step2:checked ~ .progress-bar .progress-step:nth-child(2),
#radio-step3:checked ~ .progress-bar .progress-step:nth-child(3) {
background-color: #4285f4;
color: white;
}
/* Show current step based on selected radio button */
#radio-step1:checked ~ .step1,
#radio-step2:checked ~ .step2,
#radio-step3:checked ~ .step3 {
display: block;
animation: fadeIn 0.5s;
}
/* Button styling */
.btn {
padding: 10px 15px;
background-color: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
display: inline-block;
}
.btn.prev {
background-color: #ccc;
}
.form-controls {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
This approach leverages the CSS adjacent sibling selector (~
) to show and hide form steps based on which radio button is selected. The radio buttons themselves are hidden, but their labels function as navigation buttons.
For a live demonstration of this technique, check out this CodePen example of a CSS-only multi-step form.
For more about CSS selectors and how they work, visit CSS-Tricks’ Complete Guide to CSS Selectors.
Technique 3: Using Checkbox Hack for Multi-Step Forms
A third approach uses the CSS “checkbox hack” to control the visibility of form sections without JavaScript. This method is explained in detail on CSS-Tricks’ article on the checkbox hack.

Implementation:
<div class="multi-step-form">
<form action="/submit-form" method="post">
<!-- Hidden checkboxes to control the state -->
<input type="checkbox" id="activate-step1" class="step-activator" checked>
<input type="checkbox" id="activate-step2" class="step-activator">
<input type="checkbox" id="activate-step3" class="step-activator">
<!-- Steps -->
<div class="form-step step1">
<h2>Step 1: Personal Information</h2>
<div class="form-group">
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="dob">Date of Birth</label>
<input type="date" id="dob" name="dob" required>
</div>
<div class="form-nav-buttons">
<label for="activate-step2" class="btn next">Next</label>
</div>
</div>
<div class="form-step step2">
<h2>Step 2: Contact Details</h2>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" required>
</div>
<div class="form-nav-buttons">
<label for="activate-step1" class="btn prev">Previous</label>
<label for="activate-step3" class="btn next">Next</label>
</div>
</div>
<div class="form-step step3">
<h2>Step 3: Review & Submit</h2>
<p>Please review your information before submitting.</p>
<div class="form-nav-buttons">
<label for="activate-step2" class="btn prev">Previous</label>
<button type="submit" class="btn submit">Submit Form</button>
</div>
</div>
</form>
</div>
And the CSS:
/* Hide step activator checkboxes */
.step-activator {
display: none;
}
/* Hide all steps by default */
.form-step {
display: none;
}
/* Show steps based on checkbox state */
#activate-step1:checked ~ .step1 {
display: block;
}
#activate-step2:checked ~ .step2 {
display: block;
}
#activate-step3:checked ~ .step3 {
display: block;
}
/* Rest of the styling (similar to previous examples) */
.form-nav-buttons {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.btn {
padding: 10px 15px;
background-color: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
display: inline-block;
}
.btn.prev {
background-color: #ccc;
}
Best Practices for HTML-Only Multi-Step Forms
When implementing multi-step forms without JavaScript, consider these best practices:
- Use proper HTML semantics – Ensure form elements are properly labeled and structured
- Provide visual feedback – Use progress indicators to show users their position in the form flow
- Maintain data continuity – Ensure all form fields are submitted together
- Test accessibility – Verify the form works with screen readers and keyboard navigation
- Add validation hints – Provide clear instructions on required fields
- Optimize for mobile – Ensure the form is responsive and touch-friendly
According to the Nielsen Norman Group’s form design research, clear labeling and feedback are critical for form usability.
Implementing Proper Form Validation
HTML5 offers built-in form validation attributes that work without JavaScript:
<input type="email" id="email" name="email" required pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$">
<span class="validation-message">Please enter a valid email address</span>
For more on HTML5 form validation, check out W3Schools’ guide on form validation.
Making Multi-Step Forms Responsive
Responsive design ensures your forms work well on all devices:
@media (max-width: 768px) {
.multi-step-form {
padding: 15px;
}
.form-nav {
flex-direction: column;
}
.btn {
padding: 12px 15px;
font-size: 16px;
}
}
The Google Mobile-Friendly Test can help evaluate your form’s mobile responsiveness.
Limitations of HTML-Only Multi-Step Forms
While these techniques work without JavaScript, they do have some limitations:
- No field validation between steps – Without JavaScript, you cannot validate fields before proceeding to the next step
- Limited animation options – CSS transitions and animations are more limited than JavaScript-based animations
- No conditional logic – Without JavaScript, you cannot show/hide fields based on user input
- Form state is not saved – If the page is refreshed, form progress is lost
- Browser compatibility varies – Some techniques may not work consistently across all browsers
According to Can I Use, support for advanced CSS selectors varies across browsers, so testing is essential.
When to Use HTML-Only vs. JavaScript Solutions
HTML-only multi-step forms are ideal for:
- Simple linear forms with few fields
- Projects with strict performance requirements
- Sites where accessibility is paramount
- Basic data collection without complex validation needs
JavaScript solutions are better for:
- Complex forms with conditional logic
- Forms requiring field validation between steps
- Applications where user experience is critical
- Forms where you need to save progress
For more complex form requirements, consider lightweight JavaScript libraries like Pristine.js for validation or FormValidation.io for comprehensive form handling.
Improving Form Accessibility
The Web Content Accessibility Guidelines (WCAG) provide important standards for making forms accessible:
<label for="name" id="name-label">Full Name</label>
<input type="text" id="name" name="name" required aria-labelledby="name-label" aria-required="true">
Summary – How to Create a Multi-Step Forms in HTML Without JavaScript
Creating multi-step forms without JavaScript is entirely possible using CSS techniques like the :target
selector, hidden radio buttons, or checkbox hacks. These methods provide lightweight, accessible alternatives to JavaScript-heavy implementations, making them ideal for performance-critical applications or situations where JavaScript might be disabled.
By understanding the various techniques, their limitations, and best practices, you can create effective multi-step forms that enhance user experience while maintaining compatibility and accessibility.
FAQs – How to Create a Multi-Step Forms in HTML Without JavaScript
Can I validate form fields between steps without using JavaScript?
No, client-side validation between steps requires JavaScript. However, you can use HTML5’s built-in validation attributes like required
, pattern
, and type
for basic validation when the form is submitted. The W3C HTML Validator can help check your form’s HTML validity.
How do I make sure all form data is submitted together?
All fields in the form will be submitted when the submit button is clicked, regardless of which step they appear in. The multi-step interface is purely visual – all fields exist in the same HTML form. Learn more about form submission at Mozilla’s HTML forms guide.
Will these techniques work on all browsers?
Most modern browsers support these CSS techniques. The :target
selector has excellent browser support, while the checkbox/radio button method works in virtually all browsers. Always test your implementation across different browsers and devices. Browser Stack offers cross-browser testing tools.
How can I improve accessibility for these multi-step forms?
Use proper semantic HTML, ensure keyboard navigation works correctly, include ARIA attributes where appropriate, and test with screen readers. Also, consider adding skip links or shortcuts for users who may have difficulty with multi-step interfaces. The WebAIM accessibility guide offers detailed advice on form accessibility.