So You Want To Be A Wizard
Multi-step forms in Rails are a pain if you try to roll your own. I spent three years fighting with session storage, URL management, and partial renders before someone at work told me about the wizard gem. It took me about ten minutes to replace a week of debugging. https://github.com/abensasson/wizard It's a gem that lets you define a series of steps in a controller and handles the navigation between them. The basic setup is straightforward. You add the gem to your Gemfile, run bundle install, then scaffold a wizard controller.
Here's what a minimal implementation looks like: gem 'wizard' In your controller:
class SignupWizardController < ApplicationController The gem stores each step's parameters in the session by default. That means if someone closes their browser halfway through, the data is still there when they come back. Most people don't realize how important that is until they've lost hundreds of users to abandoned forms.
wizard :signup do
step :personal
step :address
step :payment
end
def next
next_step
end
def finish
process the completed form
end
end
Get the Full Details

How it Actually Works
When you define a wizard with steps, the gem creates a state machine. Each step has its own model or form object. The current step is tracked in the session under session[:wizard_state]. When you call next_step, it validates the current step's data, moves to the next step, and re-renders the appropriate view. One thing beginners miss: you can define validation inside each step's model or form class, not in the controller. Keep them separate. If validation lives in the controller, your code becomes unreadable within a month. There's also step_params which lets you filter which attributes from the current step get stored. Without this, you end up passing every parameter from every step into every step's model, which causes conflicts when two steps have fields with the same name.
I ran into a specific problem last year where two steps both had a field called "name" — one was a customer's full name, the other was a product name. The gem was overwriting one with the other during the transition between steps. The workaround was to namespace the parameters using strong parameters in each step's controller action: def step_params Then in your model, map those namespaced params to the correct attributes. It's an extra step but it prevents silent data corruption.
params.require(:personal).permit(:full_name, :email)
end
Common Pitfalls
Step three of five. The form submits. But nothing happens on the next button click. This almost always comes down to CSRF token expiration. The gem doesn't automatically refresh tokens between steps. If your session times out or the user stalls for more than a few minutes, the token becomes invalid. Add a meta tag in your layout that refreshes or include a hidden field with a fresh token on each step render. Another issue is file uploads. The wizard gem doesn't handle uploaded files well across steps. File data doesn't survive session serialization properly. If your wizard needs file uploads, store them temporarily in the filesystem or S3 before the final submission, and only keep references in the session.

Testing
Testing wizards is the part most people skip. Write a request spec that simulates going through all steps. Make sure the final submission persists all data correctly. Also test the edge case where someone goes backward — the gem supports previous_step but you need to make sure your models can handle re-saving with potentially different data. I usually write specs for each individual step too. Step-level tests catch validation bugs faster than integration tests because they isolate the problem to a single step's controller and view.
When Not to Use It
If your wizard has fewer than three steps, don't bother. The overhead isn't worth it. Just use a single form with fields_for or a simpler approach. If you need complex branching logic where the steps change based on previous answers, the basic wizard gem gets messy. In that case, consider apartment for multitenancy or build a custom state machine with state_machine gem instead. Also, if your users are coming from mobile devices with flaky connections, consider adding auto-save between steps. The default behavior waits for explicit next button clicks, which means data loss if the connection drops mid-submission.
Alternatives
multi_step_form is another option but it's less maintained. wizard by pboling is similar but requires more manual setup. For most Rails projects, abensasson's wizard gem strikes the best balance between simplicity and flexibility. The documentation could be better. The README covers basics but skips the tricky parts like namespacing params and handling file uploads. Don't expect to find solutions there. Check the issues tab on GitHub — most edge cases have been discussed by other people who hit the same problems.