Recently I've been adding fullscreen search modals to all of my Rails apps. It's surprisingly easy to add this functionality using a bit of Turbo, Stimulus and Postgres. I'll show you how you can add that to your Rails app too!
I'm using the following tools to create a search modal:
PostgreSQL My preferred database (it supports full-text search out of the box), but any database will do.
Hotkeys.js An optional js-library to add hotkey support to quickly open your search modal. Optional, but highly recommended for that finishing touch.
TailwindCSS I know. Not everyone loves Tailwind. It's totally optional. The code examples will include some Tailwind classes, but feel free to write your own CSS.
Prerequisites
If your app already supports modals using Hotwire: great! If not, read this article to learn how to add modals to your Rails app.
1. Create a SearchController
Begin by creating a simple controller as a starting point.
class SearchController < ApplicationController
def new
end
def create
@results = [] # We'll replace this later with actual search functionality
render :new
end
end
And add the appropriate route to routes.rb.
resource :search, controller: "search"
2. Search modal view
Create a new view in app/views/search/new.html.erb and create your search form like so:
Note that we already have a search_results turbo frame in place to render our results. The search form points to turbo_frame: :search_results to make sure the response is rendered in the :search_results turbo frame.
3. Add results partial
Create a new view partial in app/views/search/_results.html.erb so you can render the search results:
<%= turbo_frame_tag :search_results do %>
<div class="py-2">
<% @results.each do |result| %>
<%= link_to result, class: "block px-2 leading-tight", data: {turbo_frame: "_top"} do %>
<div class="flex items-center justify-between w-full px-4 py-3 rounded-md bg-gray-400 bg-opacity-0">
<div>
<div class="font-medium flex items-center">
<%= result %>
</div>
</div>
</div>
<% end %>
<% end %>
</div>
<% end %>
Obviously, I'm just rendering result in this example, which can be anything depending on your app. Let your imagination run wild! Make sure that any links use data-turbo-frame="_top", or Turbo will try to load the response inside your turbo frame.
4. Search for something
Let's add some actual code that searches your database. Since this is highly dependent on your app, I'll give you an example of Spina CMS. In Spina Pro you can search for pages, simply by doing Spina::Page.search("some search term"). If you don't have a proper search method yet, you can keep it simple by doing something like this:
YourRecord.where("some_column LIKE ?", "%#{params[:query]}%").limit(10)
A simple LIKE-query actually works really well for many use cases.
Add your search logic to the Search#create action:
# search_controller.rb
def create
@results = Spina::Page.search(params[:query]).limit(5)
render :new
end
It looks a bit strange to use render :new here, but Turbo makes sure it only replaces your results Turbo frame.
5. Add a link to your modal
Add a link somewhere in your layout to open your search modal. I like to add a search icon in my navigation bar.
<%= link_to new_search_path, data: {turbo_frame: "modal"} do %>
<!-- Some search icon, try heroicons.com -->
<% end %>
Try out your search modal!
You should now have a basic, functioning search modal.
You can submit your search form by hitting return. That's not really user-friendly though. Let's add some sprinkles with Stimulus!