ViewComponent is a Rails library for building reusable front-end components.
I prefer to colocate my component-specific CSS within the component's directory.
For example, table
is stored like this:
app/
├─ components/
├─ table/
├─ table_component.rb
├─ table_component.html.erb
├─ table_component.css
├─ table_component_controller.js
I think this layout is clean and easy to understand.
I heavily use Tailwind CSS, and I want any additional CSS files to be run through Tailwind's pipeline so I can use the @apply
directive.
For my Stimulus.js setup, see this gist.
- A Rake task finds all component CSS, and generates a CSS file that contains all the imports.
- Import the generated file into the main CSS file.
- An optional process watches the
app/components
directory and regenerates the import file when changes are made.
Create lib/tasks/component_css.rake
:
namespace :build do
desc "Generate _components.css with imports for all component CSS files"
task :component_css do
components_dir = Rails.root.join("app/components")
css_files = Dir.glob("#{components_dir}/**/*.css")
imports = css_files.map { |file| "@import \"../../components/#{file.gsub("#{components_dir}/", "")}\";" }
output_file = Rails.root.join("app/assets/stylesheets/_components.css")
file_leading_comment = "/* This file is auto-generated by the task `rake build:component_css`.\n Instead of modifying it directly, re-run the task. */\n\n"
File.write(output_file, file_leading_comment + imports.join("\n"))
puts "✅ Updated _components.css with #{css_files.count} components."
end
end
Running rake build:component_css
will generate the file app/assets/stylesheets/_components.css
.
Make sure to check it in to your source control. Do not add this to
.gitignore
, even though it's a generated file.
Modify app/assets/tailwind/application.css
:
@import "../stylesheets/_components.css" layer(utilities);
Re-run rake build:component_css
after you add any new component CSS files.
(If you don't want to remember this, add a custom file watcher to your dev environment in the next step.)
We'll use the listen
gem to build a file watcher.
bundle add listen --group=development
- Create the watch script and make it executable:
touch bin/component-css
chmod +x bin/component-css
- Write the watch script:
#!/usr/bin/env ruby
require 'listen'
# Path to watch
watch_dirs = ['app/components']
# Callback function
listener = Listen.to(*watch_dirs, only: /\.css$/) do |_modified, _added, _removed|
puts "[WATCHER] Detected changes in component CSS files. Running rake task..."
system("bin/rails build:component_css")
end
puts "[WATCHER] Watching component CSS..."
listener.start
sleep
- Add to
Procfile
(orProcfile.dev
):
component_css: bin/component-css
Finally, restart the dev server: bin/dev
- ✅ All component CSS is automatically inlined into your main CSS file
- ✅ Tailwind can be used in all component CSS files
- ✅ Any new component CSS files will be automatically detected and added to the build.
💡 Note: The app build process doesn't need to be changed, because the generated
_component.css
CSS file is checked in to source control