How to create seamless modal forms with Turbo Drive
07 Oct 2021
Turbo Frames inspired us to ask for a piece of html to work regardless if it is rendered on it’s own page, or as part of another page. I’m borrowing the same idea to apply it to modals. Goal is to not introduce any changes to the backend code (no Turbo Streams), but still be able to submit forms and see validation errors.
This has the benefits of:
easy development - one doesn’t need to keep clicking the “open modal” button to open and test the modal every time
ability to have a dedicated page for the modal content
The solutions consists of a few pieces:
Send additional header to tell the backend that we want to render the modal in a “modal context”
If response comes up as a success -> follow the generic turbo drive behaviour, which is usually to redirect to the next page
If response from server contains validation errors -> update the form with the html from the server containing the validation errors
On the Rails side:
check if a “modal variant” of the page is requested and serve that variant instead
“modal variant” includes the html for wrapping the modal
remove the layout for “modal variant” requests
Let’s go through those one by one:
I’ll use the Modal from Tailwind Stimulus Components, but if you don’t fency including the library, feel free to copy paste the code from there. It has not other dependencies.
Create your own modal controller:
The tickiest bit of it all is in the populate() which is called after a form on the modal is submitted.
If the request is successful, the standard Turbo Drive behaviour will be followed (usually redirect to the next page)
If the request is not successful however (422 :unprocessable_entity), then replace the modal html with the one sent from the server, which would likely include the validation errors.
Pay attention how we send an additonal custom header - X-Show-In-Modal - before each request.
Check for the presence of a special header X-Show-In-Modal, and if so:
render the page without the layout
render an alternativa variant of the page
We can now either call a modal with the contents of the #new action as well render the form on its own page as customary.
Call the modal from the index page
Render the form as ordinary directly on the New page
This is our nice and reusable modal with all accessibility properties catered for:
The “modal version” of the html includes the html for rendering the modal.
Nothing special about the non-“modal version” of the page
Any validation errors correctly update the modal. If no errors, a redirect is followed.
That’s all! Hope it’s useful!
Send me a message at email@example.com to let me know if you like it.