Β· Guides Β· 3 min read
When a checkbox isn't enough - toggles in Rails Forms with Tailwind
How to create a CSS-based toggle button inside a Rails form with Tailwind CSS
If youβre creating a form the standard format for a boolean (true or false, on or off) field is to use a checkbox.
This works well, if youβre filling in a form and clicking submit at the bottom, like when we create a new webhook in MailPace:
But what if you donβt have a submit button? What if you let users make an On/Off change in line with your content? Then you need a solution that allows the user to make the change, and simultaneously show the state of the change in one place.
We use this in our email webhooks, which are triggered when an email changes state in the MailPace system. The toggle is used to enable or disable them, and they look like this:
How?
In Rails with Tailwind itβs easy. Just create a normal Rails form like this:
<%= form_with model: model_name do |form| %> ... <% end %>
And inside it add an inline-flex
label:
<%= form_with model: model_name, class: "inline" do |form| %><label class="relative inline-flex items-center cursor-pointer"> ... </label><% end %>
Inside that we put our check box, but hide it with sr-only
<%= form_with model: model_name, class: "inline" do |form| %><label class="relative inline-flex items-center cursor-pointer"> <%= form.check_box :enabled, checked: endpoint.enabled, class: "sr-only peer" %></label><% end %>
Then lets put the actual Toggle in:
<%= form_with model: model_name, class: "inline" do |form| %><label class="relative inline-flex items-center cursor-pointer"> <%= form.check_box :enabled, checked: endpoint.enabled, class: "sr-only peer" %> <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600" ></div></label><% end %>
Finally, letβs add an onChange
handler to the checkbox to submit the form when it changes state:
<%= form_with model: model_name, class: "inline" do |form| %><label class="relative inline-flex items-center cursor-pointer"> <%= form.check_box :enabled, checked: endpoint.enabled, class: "sr-only peer", onchange: "this.form.requestSubmit();" %> <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600" ></div></label><% end %>
On the server side, just handle this like a normal form. Ideally using Turbo to avoid full page reloads, and a nice toast or flash notification so the user knows itβs changed something immediately.
Why donβt browsers support this natively?
I have no idea. If anyone wants to make it happen, please do!
Got more questions, comments or feedback?
Contact us (by email of course): support@mailpace.com