How to Build a Multi-Step Form with Save and Resume in WordPress (Without a Plugin)
Why save-and-resume forms exist
Long forms have a known abandonment problem. The longer the form, the more people leave. For NeuroNet NZ, the platform I built for child evaluation assessments, the problem was sharper than usual: parents and professionals filling in the form were typing thoughtful answers about a child's behaviour and history. Many needed to step away, talk to a partner, look up a document, and come back hours or days later. A form that loses progress is unusable in that context.
This article walks through the architecture I built for that platform and the WordPress-native techniques that make it work without a single third-party form plugin.
What does "multi-step with save and resume" actually mean?
Three properties:
- Multi-step. The form is split into logical sections shown one at a time, with progress indication.
- Save. Every step's data is persisted to the server after the user completes it, not at the end.
- Resume. The user can close the browser, return on a different device, identify themselves, and continue exactly where they left off — same answers, same step.
That third property is the hard one. It requires identity, storage, and careful handling of partial state.
The data model
One custom post type stores submissions. One row per submission. Status field captures whether the submission is in progress or complete. Post meta holds the actual answers, keyed by question.
The custom post type:
register_post_type( 'evaluation', [
'labels' => [ 'name' => 'Evaluations' ],
'public' => false,
'show_ui' => true,
'supports' => [ 'title' ],
'capability_type' => 'evaluation',
'map_meta_cap' => true,
] );
Capabilities are mapped to a custom capability so that ordinary users cannot access submissions through the admin.
The submission record gets created the moment the user starts the form, before any answers are submitted. That gives every submission a stable WordPress post ID that can be referenced from then on.
How does the user identify themselves on resume?
Three options, picked by use case:
- Account creation. Heaviest but most secure. The user creates a WordPress account before starting. Resume is just "log in and continue".
- Magic link. The user enters their email at the start. They get an email with a signed link. Returning means clicking that link. This is what NeuroNet NZ uses.
- Anonymous session. The user gets a long random token in a cookie. Useful only when the user will resume on the same device.
Magic link is the sweet spot for sensitive forms because it removes the password burden but still ties the submission to a verified email address.
Magic link implementation
When the user enters their email at the start:
- Create the submission post.
- Generate a long random token (e.g. 32 bytes via
random_bytes()hex-encoded). - Store the token hashed (SHA-256) in post meta against the submission.
- Email the user a link of the form
https://example.com/resume/?id={post_id}&token={raw_token}.
On resume:
- Look up the submission by post ID.
- Hash the supplied token and compare against the stored hash using
hash_equals()for timing-safe comparison. - On match, set a session cookie tying the browser to that submission ID for the next few hours.
- Redirect to the form at the saved step with the saved answers populated.
Step rendering: server-rendered with progressive enhancement
The form is server-rendered. Each step is a WordPress page template that reads the submission's saved answers, displays the relevant inputs, and submits via a standard POST to admin-post.php.
JavaScript enhances the experience (client-side validation, smooth step transitions, autosave on blur) but the form works without JavaScript. That matters because the audience for evaluation forms is broader than typical commercial software — some users are on older browsers or have JS blocked by school networks.
On each POST:
- Verify the nonce.
- Verify the user identity matches the submission (token cookie + DB lookup).
- Sanitise each input.
sanitize_text_field()for short text,wp_kses_post()for rich text, custom validators for dates, emails, and structured answers. - Save each answer to post meta with a deterministic key (
_evaluation_answer_q1_dobfor example). - Advance the saved step counter.
- Redirect to the next step.
Autosave
For long open-text questions, the autosave-on-blur enhancement is important. Without it, a user typing for 15 minutes on a question and then losing power loses the work. The JS sends a small AJAX request to a dedicated REST endpoint every time the input loses focus.
The REST endpoint:
register_rest_route( 'evaluation/v1', '/autosave/(?P<id>\d+)', [
'methods' => 'POST',
'permission_callback' => 'eval_can_edit_submission',
'callback' => 'eval_handle_autosave',
] );
Server-side, the same identity check applies. Autosave never advances the step counter — it only updates the current step's pending answers.
Privacy and data retention
Submission data on an evaluation platform is sensitive. The architecture above includes a retention policy: incomplete submissions older than X days are deleted automatically via a daily WP-Cron job. Complete submissions are kept according to the data controller's policy.
Other essentials:
- HTTPS only (enforced at the web server level).
- Admin access to submissions logged via a custom audit-log post type.
- Database backups encrypted at rest (use the VPS provider's encryption-at-rest option).
What I learned shipping this on NeuroNet NZ
Three lessons that surprised me:
- Users genuinely use the resume feature. Roughly half of the submissions on NeuroNet NZ were paused at least once.
- Email deliverability was a real concern. Magic links must go through a transactional email provider (Postmark, Mailgun) rather than the default
wp_mail. Otherwise the link ends up in spam and the user thinks the form is broken. - The non-clinical UI was more important than the engineering. Users filling in sensitive forms are anxious. The visual language has to feel calm, supportive, and patient. Tone work mattered more than any technical detail.
Frequently asked questions
Can I do this with Gravity Forms or Forminator?
Both plugins have save-and-resume features. They are reasonable for general business forms. The custom architecture above is right when you need full control over identity, data model, retention, and audit logging — typical for healthcare, education, or any regulated sector.
How long does it take to build?
A first version of the magic-link flow plus a 5-step form takes 2-3 weeks of full-time work in WordPress. Add another week for autosave and audit logging.
What hosting do I need?
A VPS with PHP 8.2 and a transactional email service (Postmark, Mailgun). Shared hosting is a poor fit because of mail-deliverability constraints and the need to run WP-Cron reliably.
Also read
- Case study: NeuroNet NZ
- WordPress on VPS: 99% Uptime Stack
- WordPress REST API: Building Headless WordPress with React
Need a complex form built?
Multi-step forms with save and resume are one of my regular project types. If you have an evaluation, intake, or onboarding flow that needs to feel polished and not lose user data, get in touch.