Authentication/Devise
In the version 3 of Rails UI, direct integration with Devise was dropped in favor of a more flexible approach that is based on code snippets you copy and paste into your app.
If you use Devise, these snippets should give you a head start on all the views generated when running/having run rails generate devise:views. Not using Devise? Check out the static version.
Here are the pages you'll find in the Devise authentication system:
Consider extracting the layout partial to a reusable partial. For the examples shown it's assumed you've added this partial in the app/views/devise directory.
<!-- app/views/devise/_auth_layout.html.erb -->
<div class="sm:h-[calc(100vh_-_52px)] pt-10 sm:pt-0 flex flex-col items-center justify-center bg-cover bg-center px-4" style="background-image: url('<%= asset_url('railsui/fusion.png') %>')" >
<div class="sm:flex-1 flex flex-col justify-center sm:w-[428px] w-full">
<div>
<div class="flex justify-center">
<%= link_to root_path do %>
<%= icon "logo", custom_path: "/railsui/logo.svg", class: "w-10 h-auto" %>
<% end %>
</div>
<div class="mt-6">
<%= yield :masthead %>
</div>
<div class="bg-white shadow-xs rounded-lg p-8 border border-slate-300/60">
<!-- Add or yield form content here -->
<%= yield %>
<!--
Add additional provider SVG icons in app/assets/images/railsui/omniauth as necessary.
Default options include: Google, LinkedIn, X, Facebook, GitHub, YouTube, Instagram.
**You will need to install additional dependencies
not included in Rails UI for omniauth support.**
-->
<% if devise_mapping.omniauthable? && %w{ registrations sessions }.include?(controller_name) %>
<hr class="my-6"/>
<% resource_class.omniauth_providers.each do |provider| %>
<div class="my-2">
<%= button_to omniauth_authorize_path(resource_name, provider), class: "btn btn-white w-full", data: { turbo: false } do %>
<%= icon "#{provider.gsub(/\s+/, '').downcase}", custom_path: "/railsui/omniauth/#{provider.gsub(/\s+/, '').downcase}.svg", class: "mr-2 size-5" %>
<span>"Sign in with <%= OmniAuth::Utils.camelize(provider) %></span>
<% end %>
</div>
<% end %>
<% end %>
</div>
<div class="mt-4">
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>
/ app/views/devise/_auth_layout.html.erb
.sm:h-[calc(100vh_-_52px)].pt-10.sm:pt-0.flex.flex-col.items-center.justify-center.bg-cover.bg-center.px-4{style: "background-image: url('#{asset_url('railsui/fusion.png')}')"}
.sm:flex-1.flex.flex-col.justify-center.w-full{class: "sm:w-[428px]"}
%div
.flex.justify-center
= link_to root_path do
= icon "logo", custom_path: "/railsui/logo.svg", class: "w-10 h-auto"
.mt-6
= yield :masthead
.bg-white.shadow-xs.rounded-lg.p-8.border{class: "border-slate-300/60"}
= yield
- if devise_mapping.omniauthable? && %w{ registrations sessions }.include?(controller_name)
%hr.my-6/
- resource_class.omniauth_providers.each do |provider|
.my-2
= button_to omniauth_authorize_path(resource_name, provider), class: "btn btn-white w-full", data: { turbo: false } do
= icon "#{provider.gsub(/\s+/, '').downcase}", custom_path: "/railsui/omniauth/#{provider.gsub(/\s+/, '').downcase}.svg", class: "mr-2 size-5"
%span
Continue with #{OmniAuth::Utils.camelize(provider)}
.mt-4
= render "devise/shared/links"
Then in dedicated Devise views you can render the main meat and potatoes.
<%= render "devise/auth_layout" do %>
<!-- Add or yield form content here -->
<%% end %>
If you're using Devise be sure to update the _links.html.erb partial in your app to match the following.
<!-- app/views/devise/shared/_links -->
<% if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<div class="text-center mb-1">
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800" %>
</div>
<% end %>
<% if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<div class="text-center mb-2">
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800" %>
</div>
<% end %>
/ app/views/devise/shared/_links
- if devise_mapping.confirmable? && controller_name != 'confirmations'
.text-center.mb-1
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800"
- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
.text-center.mb-2
= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800"
The Hound theme has support for themed and branded Omniauth provider icons. Icons for those providers are rendered from app/assets/images/railsui/omniauth.
Here's an example of the UI with various providers:
By default Rails UI copies over a pre-styled error partial made to work out of the box and save you time during development.
The Hound theme leverages this partial for all form error rendering to keep the error/validation handling experience consistent.
You may optionally swap the default Devise error_messages partial for this one.
Reset it in an instant
1 error prohibited this post from being saved:
<% if resource.errors.any? %>
<div class="bg-rose-50 text-rose-700 sm:px-9 sm:py-6 px-6 py-6 rounded-lg mb-6 dark:bg-rose-400/10 dark:border dark:border-rose-400/20 dark:text-rose-50 text-sm" role="alert">
<div class="flex items-start gap-4">
<%= icon "shield-exclamation", class: "size-6 text-rose-700 flex-shrink-0", variant: :solid %>
<div class="flex-1">
<p class="font-bold"><%= pluralize(resource.errors.count, "error") %> prohibited this post from being saved:</p>
<ul class="list-disc mt-3 ml-4">
<% resource.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
</div>
</div>
<% end %>
- if resource.errors.any?
.bg-rose-50.text-rose-700.sm:px-9.sm:py-6.px-6.py-6.rounded-lg.mb-6.dark:border.dark:text-rose-50.text-sm{class: "dark:bg-rose-400/10 dark:border-rose-400/20", role: "alert"}
.flex.items-start.gap-4
= icon "shield-exclamation", class: "size-6 text-rose-700 flex-shrink-0", variant: :solid
.flex-1
%p.font-bold
= pluralize(resource.errors.count, "error") prohibited this post from being saved:
%ul.list-disc.mt-3.ml-4
- resource.errors.each do |error|
%li
= error.full_message