Devise Part 3: Updating Our Devise Views With Tailwind CSS

In part three of the series, we walk through how to add TailwindCSS to a Rails 7 project and spruce up our sign in page.

On this page

In the previous post (part two), we changed our Devise setup to use Redis for sessions. This will be required going forward (but feel free to swap back to the cookie store).

Today, we will take a quick pivot to beautify our sign in page using TailwindCSS and TailwindUI.

We are going to specifically implement the demo sign in component that comes for free.

Source code can be found here.

Prerequisites

  1. Basic familiarity with setting up a new Rails project.
  2. Some familiarity with TailwindCSS would be useful as we won't really cover how it works.

Getting started

We will be working from the source code found here.

# Clone and change into the project
$ git clone https://github.com/okeeffed/demo-rails-7-with-devise-series
$ cd demo-rails-7-with-devise-series

# Continue from part 2 - warning that Redis required
$ git checkout 2-using-redis-sessions

# Add required gems and setup the files
$ bundler add tailwindcss-rails
$ bin/rails tailwindcss:install

# We want to update our Devise views, so we will need to scaffold them
$ bin/rails generate devise:views

Note that since we want to update our Devise views, we will need to scaffold them. This is done in the last command of the above.

The bin/rails tailwindcss:install went ahead and added a bin/dev file and updated our Procfile.dev to run a command to watch TailwindCSS.

To see the fruits of our labor, start the server with bin/dev (instead of bin/rails s).

Updating our Devise views

I went ahead and did a lot of the work for our Devise view here in converting over the desired component to ERB.

Update app/views/devise/sessions/new.html.erb to the following:

<div
  class="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8"
>
  <div class="max-w-md w-full space-y-8">
    <div>
      <img
        class="mx-auto h-12 w-auto"
        src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg"
        alt="Workflow"
      />
      <h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
        Sign in to your account
      </h2>
      <%- if devise_mapping.registerable? && controller_name != 'registrations'
      %>
      <p class="mt-2 text-center text-sm text-gray-600">
        Or <%= link_to "sign up", new_registration_path(resource_name), class:
        "font-medium text-indigo-600 hover:text-indigo-500" %>
      </p>
      <% end %>
    </div>
    <%= form_for(resource, as: resource_name, url: session_path(resource_name),
    html: { data: { turbo: false} }) do |f| %>
    <div class="mt-8 space-y-6">
      <input type="hidden" name="remember" value="true" />
      <div class="rounded-md shadow-sm -space-y-px">
        <div>
          <%= f.label :email, class: "sr-only", for: "email-address" %> <%=
          f.email_field :email, id: "email-address", autofocus: true,
          autocomplete: "email", placeholder: "Email address", class:
          "appearance-none rounded-none relative block w-full px-3 py-2 border
          border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md
          focus:outline-none focus:ring-indigo-500 focus:border-indigo-500
          focus:z-10 sm:text-sm" %>
        </div>
        <div>
          <%= f.label :password, for: "password", class: "sr-only" %> <%=
          f.password_field :password, id: "password", autocomplete:
          "current-password", placeholder: "Password", class: "appearance-none
          rounded-none relative block w-full px-3 py-2 border border-gray-300
          placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none
          focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
          %>
        </div>
      </div>
      <% if devise_mapping.rememberable? %>
      <div class="flex items-center justify-between">
        <div class="field flex items-center">
          <%= f.check_box :remember_me, class: "h-4 w-4 text-indigo-600
          focus:ring-indigo-500 border-gray-300 rounded" %> <%= f.label
          :remember_me, class: "ml-2 block text-sm text-gray-900" %>
        </div>
        <div class="text-sm">
          <%- if devise_mapping.recoverable? && controller_name != 'passwords'
          && controller_name != 'registrations' %> <%= link_to "Forgot your
          password?", new_password_path(resource_name), class: "font-medium
          text-indigo-600 hover:text-indigo-500" %> <% end %>
        </div>
      </div>
      <% end %>
      <div class="actions">
        <%= f.submit "Log in", class: "actions group relative w-full flex
        justify-center py-2 px-4 border border-transparent text-sm font-medium
        rounded-md text-white bg-indigo-600 hover:bg-indigo-700
        focus:outline-none focus:ring-2 focus:ring-offset-2
        focus:ring-indigo-500" %>
      </div>
    </div>
    <% end %>
  </div>
</div>

You will also need to update the app/views/layouts/application.html.erb view to remove all the classes from main:

<!doctype html>
<html>
  <head>
    <title>DemoRails7WithDeviseSeries</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag
    "tailwind", "inter-font", "data-turbo-track": "reload" %> <%=
    stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%=
    javascript_importmap_tags %>
  </head>
  <body>
    <main><%= yield %></main>
  </body>
</html>

If you now refresh the page, we will have a lovely component in place of what we had before!

Before
Before

...and after:

After
After

Go through the normal flow of sign-in to confirm that it all works as expected from the previous parts.

Summary

In this part, we detoured to add in Tailwind CSS and make our sign in page look a little livelier.

Although we do not cover it, you could take this further by beautifying the other sign up page that we have (you'll notice it still looks crummy) but I won't be doing that here as we can confirm that Tailwind is up and working.

Resources and further reading

Photo credit: oscrse