Multi-Step Form Master
Thank you for your purchase! This guide shows you how to add the forms to your website, change them to fit your needs and connect them to your email.
Multi-Step Form Master is a set of 20 multi-step form templates built on one small form engine. Everything is plain HTML, CSS and JavaScript: there is no jQuery, no framework and nothing to install or compile. You write normal HTML and turn on features with data attributes.
New to code?
Follow Quick start and Using a demo form. You only need to copy files and edit text.
Developer?
Jump to Form markup, the validation reference and the JavaScript API.
Requirements
- Any website or HTML page. Works with static sites, WordPress (in a Custom HTML block or theme template), Laravel, Django, Rails, React or Vue pages, and so on.
- A modern browser (see Browser support).
- Optional, for emailing submissions: a web server with PHP 7.4 or newer and a working
mail()function. You can also use any other back end (see Other back ends).
Open the demos straight from your computer by double-clicking index.html. A server is only needed for the PHP mailer.
File structure
multistep-form-master/
├── index.html Showcase page listing all 20 demos
├── demos/ One HTML page per demo form
│ ├── job-application.html
│ ├── contact-quote.html
│ └── ... (20 files)
├── assets/
│ ├── css/
│ │ ├── core.css Form styles (required)
│ │ ├── themes.css Color themes and dark mode (required)
│ │ ├── progress-styles.css Progress indicator styles (required)
│ │ ├── layouts.css Split, sidebar, full-screen and modal layouts
│ │ ├── demo.css Demo toolbar only (not needed)
│ │ └── index.css Showcase page only (not needed)
│ ├── js/
│ │ ├── form-engine.js Core engine (required)
│ │ ├── validation.js Validation rules
│ │ ├── conditional.js Show/hide fields and skip steps
│ │ ├── components.js Uploads, ratings, signature, calendar and more
│ │ ├── save-progress.js Save and resume
│ │ ├── summary.js Review step
│ │ ├── demo.js Demo toolbar only (not needed)
│ │ └── index.js Showcase page only (not needed)
│ └── images/thumbs/ Showcase thumbnails (not needed)
├── php/
│ └── send-mail.php Emails submissions to you
└── documentation/
└── index.html This file
All JavaScript files except form-engine.js are optional plugins. Leave one out and the forms still work without that feature. For example, a form without a review step does not need summary.js.
Quick start
1. Copy the files
Copy the assets/css and assets/js folders into your website. You can skip demo.css, demo.js, index.css and index.js.
2. Add the styles in your page's <head>
<link rel="stylesheet" href="assets/css/core.css">
<link rel="stylesheet" href="assets/css/themes.css">
<link rel="stylesheet" href="assets/css/progress-styles.css">
<link rel="stylesheet" href="assets/css/layouts.css">
3. Add the scripts before </body>
<script src="assets/js/form-engine.js"></script>
<script src="assets/js/validation.js"></script>
<script src="assets/js/conditional.js"></script>
<script src="assets/js/components.js"></script>
<script src="assets/js/save-progress.js"></script>
<script src="assets/js/summary.js"></script>
4. Add a form
<form class="msf msf-card" data-msf data-msf-progress="steps"
action="php/send-mail.php" method="post">
<section class="msf-step" data-msf-step data-title="About you">
<div class="msf-field">
<label class="msf-label" for="name">Name</label>
<input class="msf-input" type="text" id="name" name="name" required>
</div>
</section>
<section class="msf-step" data-msf-step data-title="Message">
<div class="msf-field">
<label class="msf-label" for="message">Message</label>
<textarea class="msf-textarea" id="message" name="message" required></textarea>
</div>
</section>
<div class="msf-success" data-msf-success hidden>
<h2 class="msf-success__title">Thank you!</h2>
<p class="msf-success__text">We'll be in touch soon.</p>
</div>
</form>
That's it. The progress indicator, Back/Next buttons, validation and the success screen are added for you. Forms with the data-msf attribute start automatically.
While the form's action is #, submitting simulates success after a short delay. This is handy for testing. Change it to php/send-mail.php (or your own URL) when you are ready.
Using a demo form
The quickest way to start is to copy one of the 20 demos:
- Open the demo file in
demos/with a code editor (for example VS Code). - Copy everything from
<formto</form>into your page. - If the demo has a
<script>block at the bottom (for live totals, the quiz score and so on), copy that too, after the form scripts. - Delete the demo toolbar markup (
<header class="msf-topbar">) if you copied the whole page. It is only for previewing. - Change the texts, labels and options, and set the form's
action.
Want the color the demo uses? Copy data-msf-theme="..." from the demo's <html> tag onto your <html> tag or onto the form itself.
Form markup
A form is made of steps. Each step is any element with data-msf-step; its data-title is shown in the progress indicator.
| Attribute or class | Where | What it does |
|---|---|---|
class="msf" | form | Base styles. Add msf-card for the white card look, msf--narrow or msf--wide to change the width. |
data-msf | form | Starts the form automatically. Use data-msf="manual" to start it yourself with MSF.init(). |
data-msf-step | section | Marks a step. Give it class="msf-step" and data-title="...". |
data-msf-progress | form | steps, bar, dots, circle, sidebar or false. See Progress styles. |
data-msf-animation | form | slide (default), fade, rise or none. |
data-msf-labels | form | JSON with button and message texts, for example {"submit": "Send"}. See Translating text. |
data-msf-success | div | The success screen shown after submitting. Add hidden so it starts hidden. |
data-msf-confetti | form | Confetti when the success screen appears. |
data-msf-save="key" | form | Save and resume. See Save and resume. |
data-msf-goto="2" | button | Jumps to a step by index (0 is the first) or by the step's id. Add data-msf-return to come back to the current step after editing. |
data-msf-reset | button | Clears the form and starts over. |
data-msf-nav | div | Use your own navigation. Put buttons with data-msf-prev, data-msf-next and data-msf-submit inside. |
data-msf-progress | nav (inside the form) | Places the progress indicator in a custom spot, for example a sidebar. |
Every JavaScript option can also be set as an attribute: startStep becomes data-msf-start-step, scrollOnChange becomes data-msf-scroll-on-change and so on.
Field structure
<div class="msf-field">
<label class="msf-label" for="email">Email<span class="msf-required-mark" aria-hidden="true">*</span></label>
<input class="msf-input" type="email" id="email" name="email" required>
<p class="msf-help">We never share your email.</p>
</div>
Error messages are added and removed for you. Place fields side by side with <div class="msf-grid"> (two columns, one on phones), or msf-grid msf-grid--3 for three. Add msf-col-full to a field to span the whole row. Use <span class="msf-optional">(optional)</span> in a label to mark optional fields.
Field types
| Type | Markup |
|---|---|
| Text, email, phone, number, date, time | <input class="msf-input" type="..."> |
| Text area | <textarea class="msf-textarea"> (add data-msf-counter and maxlength for a character counter) |
| Select | <select class="msf-select"> |
| Choice cards (radio or checkbox) | .msf-choices > label.msf-choice > input.msf-choice__input + span.msf-choice__body. Columns: msf-choices--1, --2, --3. |
| Chips (pill buttons) | .msf-chips > label.msf-chip > input.msf-chip__input + span.msf-chip__label |
| Checkbox with text | label.msf-check > input.msf-check__input + span |
| Toggle switch | label.msf-switch > input.msf-switch__input + span.msf-switch__track + span.msf-switch__text |
| Input with prefix or button | .msf-input-group with span.msf-input-group__addon, a select or a button next to the input |
Choice cards can hold extra parts inside msf-choice__body: msf-choice__icon, msf-choice__media, msf-choice__title, msf-choice__desc, msf-choice__price, msf-choice__list and msf-choice__badge. Copy a card from any demo to see them in use.
Validation
Each step is checked when the visitor clicks Next. Errors appear under the fields, the first invalid field gets focus and screen readers announce the problem. Rules come from normal HTML attributes:
| Attribute | Checks | Message key |
|---|---|---|
required | Field filled in, option chosen or box ticked | required |
type="email" | Valid email address | email |
type="url" | Valid web address | url |
type="tel" | Phone number (digits, spaces, +, -, brackets) | phone |
type="number", min, max | Number and range (also works on dates) | number, min, max |
minlength, maxlength | Text length | minlength, maxlength |
pattern="[A-Z]{3}" | Regular expression | pattern |
data-match="#password" | Same value as another field | match |
data-rule-password="medium" | 8+ characters with letters and numbers (strong also needs upper and lower case and a symbol) | password |
accept=".pdf,.docx" | File type | filetype |
data-max-size="5MB" | File size (KB or MB) | filesize |
data-max-files="3" | Number of files | maxfiles |
data-msf-group + data-min / data-max | On a wrapper of checkboxes: how many must be ticked (data-required means at least one) | group-min, group-max |
data-rule="card" | Card number (Luhn check). Also expiry (MM/YY, not in the past) and cvc | card, expiry, cvc |
data-after="#check-in" | Date is after another date field | after |
data-min-today | Date is today or later | min-today |
Custom messages
Add data-msg-<key> to the field (or the group wrapper). Use hyphens in the key:
<input class="msf-input" type="email" name="email" required
data-msg-required="We need your email to send the quote."
data-msg-email="That email doesn't look right.">
<fieldset class="msf-field" data-msf-group data-min="1" data-max="3"
data-msg-group-max="Pick up to three, please."> ... </fieldset>
Use data-msg="..." for one message that covers every rule on that field. To change the default messages for all forms, see Translating text.
Custom rules
<script>
MSF.Validation.addRule('workEmail', function (value) {
return !/@(gmail|yahoo|hotmail)\./i.test(value);
}, 'Please use your work email address.');
</script>
<input class="msf-input" type="email" name="email" required data-rule="workEmail">
A rule receives (value, element) and returns true when valid, false to show the rule's message, or a string to show that string. Several rules can be combined: data-rule="workEmail otherRule".
Browser validation helps visitors, but it can be bypassed. Always validate again on your server. send-mail.php checks file types, sizes and email headers for you.
Components
These enhanced inputs start automatically inside any form. Copy their markup from the demo listed.
| Component | Markup and options | Demo |
|---|---|---|
| File upload | <div class="msf-upload" data-msf-upload> around an input type="file" and a label.msf-upload__zone. Supports drag and drop, multiple, accept, data-max-size, data-max-files. Compact version: msf-upload--compact. | Job Application |
| Character counter | data-msf-counter on a textarea or input with maxlength. | Job Application |
| Range slider | .msf-range[data-msf-range] with an output.msf-range__value. Options: data-prefix, data-suffix, data-format="number", data-max-suffix="+". | Contact / Quote |
| Rating | <div class="msf-rating" data-msf-rating data-name="rating">. Options: data-max, data-min, data-required, data-style="scale" (number buttons, for NPS), data-icon="heart", data-labels="Bad,OK,Great", data-low / data-high. | Customer Survey |
| Tags | <div class="msf-tags" data-msf-tags data-name="skills"> with an input.msf-tags__input. Options: data-suggestions="A,B,C", data-max, data-required. | Travel Planner |
| Signature pad | <div class="msf-signature" data-msf-signature data-name="signature" data-required>. Submits a PNG data URL; send-mail.php attaches it as an image. | Loan Application |
| One-time code | <div class="msf-otp" data-msf-otp data-name="code" data-length="6" data-required>. Supports paste. | User Registration |
| Password strength | data-msf-strength on a password input (data-msf-strength="rules" adds a checklist). data-msf-reveal adds a show/hide button. | User Registration |
| Number stepper | .msf-stepper[data-msf-stepper] with buttons data-step="-1" / data-step="1" around a number input with min and max. | Hotel Booking |
| Calendar | <div class="msf-calendar" data-msf-calendar data-name="date">. Options: data-min="today" or a date, data-max, data-max-days="60", data-disabled-days="0,6" (0 = Sunday), data-week-start="1". | Appointment Booking |
| Input masks | data-msf-mask="card", expiry, cvc or digits. Put a card input in .msf-input-wrap with a span.msf-card-brand to show the card brand. | Checkout |
| Modal | <button data-msf-modal-open="#donate"> opens <dialog class="msf-modal" id="donate">. Close with data-msf-modal-close, Esc or a click outside. | Donation |
Components that don't use a single native input (rating, tags, signature, OTP, calendar) create a hidden input named after data-name, so their value is validated, saved and submitted like any other field. Custom messages work the same way: put data-msg-required on the wrapper.
Your own component
MSF.Components.register('colorPicker', '[data-color-picker]', function (el) {
// Runs once for each matching element inside a form.
});
Conditional logic
Show a field, a group of fields or a whole step only when an earlier answer matches. Hidden fields are disabled, so they are not validated, not shown in the review and not submitted. Hidden steps are skipped and removed from the progress indicator.
<!-- A field -->
<div class="msf-field" data-msf-show-if="employment=Employed"> ... </div>
<!-- A whole step -->
<section class="msf-step" data-msf-step data-title="Insurance" data-msf-show-if="insured=yes"> ... </section>
<!-- The opposite -->
<div class="msf-field" data-msf-hide-if="country=US"> ... </div>
| Condition | True when |
|---|---|
name | The field has any value, or the checkbox is ticked |
name=value | Equal (for checkbox groups: that value is one of the ticked ones) |
name=a|b|c | Equal to any listed value |
name!=value | Not equal |
name>5, <, >=, <= | Numeric comparison |
a=1 && b=2 | Both are true |
a=1 || b=2 | Either is true (&& is checked before ||) |
Use the field's name without []. Values are compared as text, exactly as written in the option's value.
Save and resume
Add data-msf-save="any-unique-key" to the form. Answers are saved in the visitor's browser as they type, and restored with a "Welcome back" notice and a Start over button when they return.
| Attribute | What it does |
|---|---|
data-msf-save="key" | Turns saving on. Use a different key for each form. |
data-msf-save-days="7" | How long answers are kept (default 7 days). |
data-msf-save-banner="false" | Restore silently, without the notice. |
data-msf-nosave | On a field: never save it. |
File uploads, passwords and signatures are never saved. Mark card numbers and other sensitive fields with data-msf-nosave. Saved answers are deleted after a successful submit. Data never leaves the visitor's device.
Review step
Put <div data-msf-summary></div> in your last step. Each time that step opens it lists all earlier answers, grouped by step, with an Edit button that jumps back and returns to the review afterwards.
| Attribute | What it does |
|---|---|
data-summary-label="Phone" | On a field or group: the label to show |
data-summary-combine="Phone" | On a wrapper: show its fields as one row (for example country code and number) |
data-summary-skip | On a field or step: leave it out |
data-summary-text | On an element inside a choice: use its text instead of the raw value |
data-msf-summary="1,2" | On the panel: only include these steps (0 is the first) |
Success screen
The element with data-msf-success replaces the steps after a successful submit. Put anything inside: a message, buttons, a summary. Useful classes: msf-success__icon, msf-success__title, msf-success__text, msf-success__actions. A button with data-msf-reset lets visitors fill the form in again. To redirect to a thank-you page instead:
document.getElementById('my-form').addEventListener('msf:success', function () {
window.location.href = '/thank-you/';
});
Progress styles
| Value | Looks like | Demo |
|---|---|---|
data-msf-progress="steps" | Numbered circles with step names (default) | Job Application |
data-msf-progress="bar" | Progress bar with "Step 2 of 5" and percentage | Contact / Quote |
data-msf-progress="dots" | Small dots, great for short or narrow forms | User Registration |
data-msf-progress="circle" | Circular percentage ring with the current step name | Hotel Booking |
data-msf-progress="sidebar" | Vertical list for the sidebar layout | Client Onboarding |
data-msf-progress="false" | No indicator |
Completed steps can be clicked to go back. Turn that off with data-msf-clickable-steps="false".
Layouts
Centered card
class="msf msf-card" inside any container. Most demos use this.
Split screen
A brand panel beside the form: .msf-split with .msf-split__aside and .msf-split__main. See Contact / Quote.
Sidebar
class="msf msf-card msf-with-sidebar" with aside.msf-sidebar and div.msf-content. See Course Enrollment.
Full screen
One question at a time: <body class="msf-page--fullscreen"> and class="msf msf-fullscreen". See Customer Survey.
Modal popup
Any form inside dialog.msf-modal. See Donation.
Your own
Use class="msf" without msf-card and the form fits into any container you have.
Colors and themes
Pick one of eight built-in themes with an attribute on <html>, or on a single form:
<html data-msf-theme="emerald">
<!-- indigo (default), violet, blue, teal, emerald, rose, orange, slate -->
Or use your exact brand color:
:root {
--msf-primary: #e11d48; /* buttons, focus, selected options */
--msf-primary-hover: #be123c;
--msf-primary-contrast: #ffffff; /* text on primary buttons */
}
Lighter tints of the primary color (hover backgrounds, focus rings) are calculated automatically.
Dark mode
Set data-msf-mode on <html>: light, dark or auto (follows the visitor's system setting). Add a toggle button anywhere on the page:
<button type="button" class="msf-theme-toggle" data-msf-theme-toggle aria-label="Dark mode">...</button>
The choice is remembered. To avoid a flash of the wrong colors on load, copy the small inline script from the <head> of any demo page.
RTL languages
Set dir="rtl" on <html> (and the right lang). The layout, progress indicators, arrows and animations mirror automatically. Try the RTL button in any demo's toolbar.
Customizing CSS
Don't edit the included CSS files, so you can update to new versions easily. Instead, add your own stylesheet after them and override the design tokens or classes:
:root {
--msf-font: "Poppins", system-ui, sans-serif;
--msf-radius: 8px; /* card corners */
--msf-radius-md: 6px; /* inputs and buttons */
--msf-control-height: 50px; /* input and button height */
--msf-bg: #f8fafc; /* page background */
--msf-surface: #ffffff; /* card background */
--msf-border: #e2e8f0;
}
All classes start with msf-, so they won't clash with your site's styles. The forms use your site's font when you set --msf-font: inherit;.
JavaScript API
Starting a form manually
<form id="signup" class="msf" data-msf="manual"> ... </form>
<script>
var form = MSF.init('#signup', {
progress: 'bar',
animation: 'fade',
onSubmit: function (data, formData) {
// Send the answers yourself. Return a Promise.
return fetch('/api/signup', { method: 'POST', body: formData }).then(function (r) { return r.json(); });
}
});
</script>
Options
| Option | Default | Description |
|---|---|---|
startStep | 0 | Index or id of the first step to show |
progress | 'steps' | Progress style, or false |
clickableSteps | true | Allow jumping back to completed steps from the progress indicator |
animation | 'slide' | 'slide', 'fade', 'rise' or 'none' |
validate | true | Validate each step before moving on |
focusOnChange | 'field' | Focus the first field ('field'), the step heading ('heading') or nothing (false) |
scrollOnChange | true | Scroll the form into view when the step changes |
keyboard | true | Enter in a text field goes to the next step |
submitUrl | null | Where to send the form (defaults to action) |
submitMethod | null | HTTP method (defaults to method, then POST) |
simulateDelay | 1200 | Fake submit delay in ms when there is no URL |
labels | Button and message texts (see Translating text) | |
onInit, onStepChange, onSuccess, onError | null | Callbacks (you can also use events) |
onSubmit | null | Custom submit: function (data, formData) returning a Promise. Resolve with {success: false, message: '...'} to show an error. |
Methods
| Method | Description |
|---|---|
MSF.init(target, options) | Starts a form (selector, element or list). Returns the instance. |
MSF.getInstance('#form') | Gets the instance of a form that has already started. |
form.next() / form.prev() | Next or previous step (next validates first). |
form.goTo(target) | Go to a step by index, id or element. |
form.validateStep(index) | Validates a step and shows errors. Returns true or false. |
form.getData() | All answers as an object ({ name: value }; checkbox groups are arrays). |
form.getFormData() | A FormData object, including files. |
form.submit() | Validates all steps and submits. |
form.reset() | Clears the form and returns to the first step. |
form.refresh() | Updates the buttons and progress indicator after you skip or un-skip steps from your own code. |
form.on('change', fn) | Shortcut for listening to msf:change. The handler receives (detail, event). |
form.destroy() | Removes the engine and its event listeners. |
MSF.setMode('dark') | Switch light/dark mode. |
MSF.confetti() | Fire the confetti effect. |
Properties: form.current (current step index), form.steps (step elements), form.isComplete.
Events
Events are dispatched on the <form> and bubble. Details are in event.detail.
| Event | When | detail |
|---|---|---|
msf:init | The form has started | |
msf:beforechange | Before the step changes. Call event.preventDefault() to stop it. | from, to, direction |
msf:change | After the step changed | from, to, step, direction |
msf:invalid | A step failed validation | step, index, fields |
msf:beforesubmit | Before sending. Call event.preventDefault() to stop it. | data, formData |
msf:success | Submitted successfully | response, data |
msf:error | Submitting failed | error, message |
msf:reset | The form was reset |
document.getElementById('quote-form').addEventListener('msf:change', function (event) {
// Example: send a Google Analytics event for each step
if (window.gtag) { gtag('event', 'form_step', { step: event.detail.to + 1 }); }
});
Translating text
For one form, set button texts with data-msf-labels:
<form class="msf" data-msf data-msf-labels='{"back": "Zurück", "next": "Weiter", "submit": "Senden", "stepOf": "Schritt {current} von {total}"}'>
For all forms, set them once in a script after the form scripts:
MSF.defaults.labels.next = 'Weiter';
MSF.defaults.labels.back = 'Zurück';
MSF.defaults.labels.submit = 'Senden';
MSF.Validation.setMessages({
required: 'Dieses Feld ist erforderlich.',
email: 'Bitte geben Sie eine gültige E-Mail-Adresse ein.'
});
MSF.Summary.labels.edit = 'Bearbeiten';
MSF.SaveProgress.labels.startOver = 'Neu beginnen';
Label keys: back, next, submit, returnToReview, stepOf, stepAnnounce, completed, errorsAnnounce, submitError, submitted. Validation message keys are listed in the validation table (in JavaScript they use camelCase, for example groupMax). Dates in the calendar follow the page's lang attribute.
PHP mailer
php/send-mail.php emails every answer to you as a neat HTML table, with uploaded files and signatures attached.
- Open
php/send-mail.phpand set'to'(your email) and'from'(an address on your website's domain) at the top. - Set your form's action:
action="php/send-mail.php" method="post" enctype="multipart/form-data". Adjust the path if your page is in a subfolder (the demos would use../php/send-mail.php). - Upload the files to your web host and send a test submission.
Built-in protection
- Honeypot: add the hidden field below to your form. Bots fill it in and are silently ignored. The Contact / Quote demo includes it.
- Rate limit: one submission per visitor every 10 seconds (configurable).
- Header injection protection, email validation for Reply-To, and file type and size checks.
- Card numbers, CVCs and passwords are never emailed (see
'skip_fields').
<div class="msf-hp" aria-hidden="true">
<label for="website-url">Leave this field empty</label>
<input type="text" id="website-url" name="website_url" tabindex="-1" autocomplete="off" data-msf-nosave data-summary-skip>
</div>
Add a hidden <input type="hidden" name="form_name" value="Job application"> to put the form's name in the email subject.
Payment demos (Checkout, SaaS Sign-up, Course Enrollment, Donation) show a card form for design purposes only. To take real payments, connect a payment provider such as Stripe or PayPal and use their secure card fields. Never send or store raw card numbers yourself.
Emails not arriving?
- Check your spam folder.
- Make sure
'from'uses your own domain. Many hosts reject other senders. - Some hosts disable
mail(). Ask your host, or use an SMTP library such as PHPMailer, or a form service (see below). - Large uploads may exceed your host's
upload_max_filesizeandpost_max_sizePHP settings.
Other back ends
The form sends a normal multipart/form-data POST with an Accept: application/json header to its action. Any back end works if it returns JSON like {"success": true} or {"success": false, "message": "Reason shown to the visitor"}. Non-JSON responses with a 2xx status also count as success.
- Form services (Formspree, Getform, Basin and similar): set their endpoint URL as the form's
action. - WordPress: point the action to a custom REST route or
admin-ajax.phphandler. - Your own API: use the
onSubmitoption or themsf:beforesubmitevent.
Accessibility
- Keyboard support throughout: Tab, Enter for the next step, arrow keys in radio groups, ratings and the calendar.
- Focus moves to the new step (or its heading) and to the first invalid field.
- Step changes, errors and submission results are announced to screen readers.
- Errors are linked to fields with
aria-describedbyandaria-invalid. - Visible focus styles, colors chosen for WCAG AA text contrast, and support for reduced motion.
- Without JavaScript all steps are shown, so the form still submits.
Keep it accessible: give every input a <label>, wrap radio and checkbox groups in a <fieldset> with a <legend>, and keep the aria-hidden on decorative icons.
Browser support
Latest two versions of Chrome, Edge, Firefox and Safari, plus Safari on iOS and Chrome on Android. Internet Explorer is not supported.
The 20 demos
| # | Demo | Highlights |
|---|---|---|
| 1 | Job Application | Numbered steps, file upload, review step, save progress |
| 2 | Contact / Get a Quote | Split screen, range slider, conditional fields, honeypot |
| 3 | Client Onboarding | Sidebar progress, calendar, time slots |
| 4 | Employee Onboarding | Multiple uploads, signature pad, input masks |
| 5 | User Registration | Password strength, OTP code, confetti |
| 6 | SaaS Sign-up / Pricing | Pricing cards, billing toggle, order summary |
| 7 | Newsletter Preferences | Narrow card, tag picker, minimum selections |
| 8 | Checkout | Live order summary, quantity steppers, payment methods |
| 9 | Hotel Booking | Circle progress, room cards, date rules |
| 10 | Appointment Booking | Inline calendar, time slots, add to calendar (.ics) |
| 11 | Event Registration | Ticket cards, live total, conditional upload |
| 12 | Customer Survey | Full-screen layout, star rating, NPS, conditional step |
| 13 | Quiz / Assessment | One question per step, score and answer review |
| 14 | Product Finder | Conditional steps, live recommendation |
| 15 | Loan Application | Payment calculator, document upload, e-signature |
| 16 | Real Estate Inquiry | Split screen, budget options for buying or renting, tags |
| 17 | Course Enrollment | Sidebar progress, payment plans, save progress |
| 18 | Donation | Modal popup, amount presets, monthly giving, confetti |
| 19 | Medical Intake | History checklist, conditional insurance, pain scale, consent signature |
| 20 | Travel Planner | Destination cards, date range, traveler steppers, interest tags |
Brand names, people and prices in the demos are fictional placeholders.
FAQ
Can I put more than one form on a page?
Yes. Each form works independently. Give each one its own id and, if you use save progress, its own data-msf-save key.
Can I use it with WordPress?
Yes. Upload the assets folder to your theme (or use a plugin that adds CSS/JS files), enqueue the files, and paste the form markup in a Custom HTML block. Point the form's action to your handler.
Can I use it with React or Vue?
Yes. Render the markup, then call MSF.init(formElement) after it mounts (use data-msf="manual") and form.destroy() before it unmounts.
How do I add or remove a step?
Copy or delete a <section class="msf-step" data-msf-step> block. The progress indicator updates itself.
How do I change the Next button text on one step?
Listen to msf:change and change the text of form.querySelector('[data-msf-next]'), or use your own navigation with data-msf-nav.
Does it work without JavaScript?
Yes. All steps are shown as one long form and it submits normally to the action URL.
Changelog
Version 1.0.0 · October 15, 2026
- Initial release with 20 demos, 5 progress styles, 4 layouts and 8 color themes.
Credits
- Icons are inline SVGs drawn in the style of Feather Icons (MIT License). See
credits.txt. - Fonts: the system font stack of each device. No web fonts are loaded.
- No images or third-party libraries are included. All demo illustrations are CSS gradients and icons.
Support
If something isn't covered here, contact us through the Support tab on the item page or the contact form on our author profile. Please include a link to the page with the problem.
Support covers questions about the item's features, help with bugs, and help with included files. It does not cover custom changes or installing third-party services, but we're happy to point you in the right direction.
If you enjoy Multi-Step Form Master, a rating on your downloads page would mean a lot. Thank you!