Tailwind on Rails: how to get the most out of it

TailwindCSS Ruby on Rails Design

Some devs hate Tailwind. Others love it. This division is also palpable among the Rails community. I default to it because it offers world class docs, incredible levels of customization, and very thoughtful design choices. Being markup-first, it pairs naturally with Rails but it requires some fine-tuning (and experience) to be truly enjoyable.

Make ERB, Tailwind IntelliSense, Prettier and Emmet work together

This is really a must and a no-brainer. If you want ERB files to work with Tailwind's wonderful VSCode extension, Prettier (best code formatter for VSCode) and Emmet (does it need an introduction?), you have to modify the settings.json file. For that press Cmd + Shift + P in macOS or Ctrl + Shift + P in Windows and then type 'Settings' to find 'Preferences: Open Settings (JSON)'. Here's the block you need to add:

  "tailwindCSS.includeLanguages": {
    "javascript": "javascript",
    "html": "html",
    "erb": "html"
  "tailwindCSS.emmetCompletions": true,
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  "tailwindCSS.experimental.classRegex": [

This will allow for autocompleting, linting, hover preview and syntax highlighting in the Tailwind parts of your ERB files ⚡️

Make Tailwind's classes work with your application's states

The greatest danger of using Tailwind is CSS code repetition and having super long class lists in your elements. Tailwind's doc recommends using template partials and components to tackle that problem, and there's also the @apply exceptional solution for small, highly reusable things.

But you can also rely on Rails helpers for that! For example, if some recurrent element relies on a state for its style, you can write :

# application_helper.rb
def status_color
  status_colors = {
    'deployed' => 'text-green-600 border-green-300 bg-green-100',
    'pending' => 'text-yellow-600 border-yellow-300 bg-yellow-100',
    'suspended' => 'text-gray-600 border-gray-300 bg-gray-100'
  status_colors[status] || ''

# An iteration of your component
<button class="status-button <%= status_color(app.status) %>">• <%= app.status %></button>

tailwind_states.png Rendering the same button with dynamic classes based on its status.

Declutter your components with inline SVG tags

If you use Tailwind's components, or other excellent external component libraries like Flowbite or Pines, you'll notice that once you start pasting parts, there's a good chance some SVG code will bloat your templates.

My advice to avoid that is pretty simple :

  1. Install the inline_svg gem.
  2. Extract each SVG code block from your page into a separate file (example: 'file.svg'), in the assets folder (for example in: 'app/assets/images/svg)
  3. In your ERB template, replace the <svg></svg> parts (and their content) with the elegant inline_svg_tag tag, which even allows for Tailwind CSS classes to be passed directly.

So this monstrosity standing in the middle of your page :

<svg width="48" height="48" fill="none" aria-hidden="true" class="text-gray-700"><path d="M17.687 42.22 40.57 29.219a4 4 0 0 0 1.554-5.36L39 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path><path d="M27.477 7.121a1 1 0 1 0-.954 1.758l.954-1.758Zm5.209 3.966.477-.879-.477.88Zm1.555 5.515-.866-.5-. 8.88l5.686 3.087.954-1.758-5.686-3.087-.954 1.758Zm6.849 7.23-12.616 22.21 1.738.987 12.617-22.21-1.74-.988Zm-1.163-4.143a3 3 0 0 1 1.166 4.136l1.732 1a5 5 0 0 0-1.944-6.894l-.954 1.758Z" fill="currentColor"></path><path d="M5 9a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4v25a9 9 0 1 1-18 0V9Z" fill="currentColor" fill-opacity="0" stroke="currentColor" stroke-width="2"></path><circle cx="14" cy="34" r="3" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></circle></svg>

becomes this :

<%= inline_svg_tag('svg/file.svg', class:'text-gray-700') %>

Bonus tip : Tailwind's team recommends the Heroicons SVG set, that works perfectly with the Inline SVG gem!

Take advantage of Tailwind's markup and data handling classes

First, last, odd, and even come in handy when you have to iterate over a list and apply different styles. In some contexts, they can save you from doing extra backend manipulations inside an .each block.

Similarly, if your styling depends on data attributes, you can save yourself some work by using the se the data-* modifier.

Instead of adding a CSS rule somewhere in your app :

[data-author="manu"] {
  background-color: black;

You can directly write this in your markup :

<div data-author='manu' class="data-[author='manu']:bg-black"></div>

Make sure it works with the latest version of Rails

This is more of a note to self but I (and some people) encountered asset loading issues when installing Tailwind with rails new app --css=tailwind, since version 2.0.8 (gem 'tailwindcss-rails', '2.0.8').

To solve this, just apply:

# development.rb
config.assets.debug = true

→ Let's get in touch

Got questions or feedback about this article? Interested in discussing your project? I'm all ears and always open to new opportunities. Shoot me an email and tell me what you have in mind.