← Back to the demos

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:

  1. Open the demo file in demos/ with a code editor (for example VS Code).
  2. Copy everything from <form to </form> into your page.
  3. 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.
  4. Delete the demo toolbar markup (<header class="msf-topbar">) if you copied the whole page. It is only for previewing.
  5. 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 classWhereWhat it does
class="msf"formBase styles. Add msf-card for the white card look, msf--narrow or msf--wide to change the width.
data-msfformStarts the form automatically. Use data-msf="manual" to start it yourself with MSF.init().
data-msf-stepsectionMarks a step. Give it class="msf-step" and data-title="...".
data-msf-progressformsteps, bar, dots, circle, sidebar or false. See Progress styles.
data-msf-animationformslide (default), fade, rise or none.
data-msf-labelsformJSON with button and message texts, for example {"submit": "Send"}. See Translating text.
data-msf-successdivThe success screen shown after submitting. Add hidden so it starts hidden.
data-msf-confettiformConfetti when the success screen appears.
data-msf-save="key"formSave and resume. See Save and resume.
data-msf-goto="2"buttonJumps 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-resetbuttonClears the form and starts over.
data-msf-navdivUse your own navigation. Put buttons with data-msf-prev, data-msf-next and data-msf-submit inside.
data-msf-progressnav (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

TypeMarkup
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 textlabel.msf-check > input.msf-check__input + span
Toggle switchlabel.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:

AttributeChecksMessage key
requiredField filled in, option chosen or box tickedrequired
type="email"Valid email addressemail
type="url"Valid web addressurl
type="tel"Phone number (digits, spaces, +, -, brackets)phone
type="number", min, maxNumber and range (also works on dates)number, min, max
minlength, maxlengthText lengthminlength, maxlength
pattern="[A-Z]{3}"Regular expressionpattern
data-match="#password"Same value as another fieldmatch
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 typefiletype
data-max-size="5MB"File size (KB or MB)filesize
data-max-files="3"Number of filesmaxfiles
data-msf-group + data-min / data-maxOn 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 cvccard, expiry, cvc
data-after="#check-in"Date is after another date fieldafter
data-min-todayDate is today or latermin-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.

ComponentMarkup and optionsDemo
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 counterdata-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 strengthdata-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 masksdata-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>
ConditionTrue when
nameThe field has any value, or the checkbox is ticked
name=valueEqual (for checkbox groups: that value is one of the ticked ones)
name=a|b|cEqual to any listed value
name!=valueNot equal
name>5, <, >=, <=Numeric comparison
a=1 && b=2Both are true
a=1 || b=2Either 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.

AttributeWhat 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-nosaveOn 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.

AttributeWhat 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-skipOn a field or step: leave it out
data-summary-textOn 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

ValueLooks likeDemo
data-msf-progress="steps"Numbered circles with step names (default)Job Application
data-msf-progress="bar"Progress bar with "Step 2 of 5" and percentageContact / Quote
data-msf-progress="dots"Small dots, great for short or narrow formsUser Registration
data-msf-progress="circle"Circular percentage ring with the current step nameHotel Booking
data-msf-progress="sidebar"Vertical list for the sidebar layoutClient 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

OptionDefaultDescription
startStep0Index or id of the first step to show
progress'steps'Progress style, or false
clickableStepstrueAllow jumping back to completed steps from the progress indicator
animation'slide''slide', 'fade', 'rise' or 'none'
validatetrueValidate each step before moving on
focusOnChange'field'Focus the first field ('field'), the step heading ('heading') or nothing (false)
scrollOnChangetrueScroll the form into view when the step changes
keyboardtrueEnter in a text field goes to the next step
submitUrlnullWhere to send the form (defaults to action)
submitMethodnullHTTP method (defaults to method, then POST)
simulateDelay1200Fake submit delay in ms when there is no URL
labels Button and message texts (see Translating text)
onInit, onStepChange, onSuccess, onErrornullCallbacks (you can also use events)
onSubmitnullCustom submit: function (data, formData) returning a Promise. Resolve with {success: false, message: '...'} to show an error.

Methods

MethodDescription
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.

EventWhendetail
msf:initThe form has started 
msf:beforechangeBefore the step changes. Call event.preventDefault() to stop it.from, to, direction
msf:changeAfter the step changedfrom, to, step, direction
msf:invalidA step failed validationstep, index, fields
msf:beforesubmitBefore sending. Call event.preventDefault() to stop it.data, formData
msf:successSubmitted successfullyresponse, data
msf:errorSubmitting failederror, message
msf:resetThe 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.

  1. Open php/send-mail.php and set 'to' (your email) and 'from' (an address on your website's domain) at the top.
  2. 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).
  3. 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_filesize and post_max_size PHP 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.php handler.
  • Your own API: use the onSubmit option or the msf:beforesubmit event.

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-describedby and aria-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

#DemoHighlights
1Job ApplicationNumbered steps, file upload, review step, save progress
2Contact / Get a QuoteSplit screen, range slider, conditional fields, honeypot
3Client OnboardingSidebar progress, calendar, time slots
4Employee OnboardingMultiple uploads, signature pad, input masks
5User RegistrationPassword strength, OTP code, confetti
6SaaS Sign-up / PricingPricing cards, billing toggle, order summary
7Newsletter PreferencesNarrow card, tag picker, minimum selections
8CheckoutLive order summary, quantity steppers, payment methods
9Hotel BookingCircle progress, room cards, date rules
10Appointment BookingInline calendar, time slots, add to calendar (.ics)
11Event RegistrationTicket cards, live total, conditional upload
12Customer SurveyFull-screen layout, star rating, NPS, conditional step
13Quiz / AssessmentOne question per step, score and answer review
14Product FinderConditional steps, live recommendation
15Loan ApplicationPayment calculator, document upload, e-signature
16Real Estate InquirySplit screen, budget options for buying or renting, tags
17Course EnrollmentSidebar progress, payment plans, save progress
18DonationModal popup, amount presets, monthly giving, confetti
19Medical IntakeHistory checklist, conditional insurance, pain scale, consent signature
20Travel PlannerDestination 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!