The "No BS" Guide to Rails 8 + HTMX + SQLite3
This guide is for developers who want to build something quickly without wading through paragraphs of theory or watching lengthy tutorial videos. What We're Building A simple app that: Uses Rails 8 and TailwindCSS 4 Has HTMX for interactivity (Turbo and Stimulus are awesome though) SQLite3 for storage, but also Solid Cable, Solid Cache, and Solid Queue Lets you add posts without page refreshes (like it's 2025 or something) Has no Node / NPM dependency whatsoever, at any step Setting up the project First, let's create a new Rails app. I'm assuming you have RVM / a recent Ruby version installed and properly set up. rails new my_rails_app --skip-javascript cd my_rails_app/ Update your Gemfile Open your Gemfile and add these gems: # Add to the main group gem "tailwindcss-rails" gem "importmap-rails" # Add to the development and test groups group :development, :test do # ... your existing development gems gem "foreman" end These gems serve specific purposes: Tailwind: Provides utility classes to avoid writing custom CSS Importmap: Enables JS module usage without complex build steps Foreman: Manages multiple processes in a single terminal Install All The Things Time to install gems and set things up: bundle install rails tailwindcss:install rails importmap:install bin/importmap pin htmx.org@2.0.4 rails generate controller Home index rails generate model Post title:string rails db:migrate db:seed rails generate controller Posts This does a bunch of stuff in one go: Installs our gems Sets up Tailwind and importmap Adds HTMX to our app Creates a Home controller with an index action Makes a Post model with a title field Creates and migrates the database Generates a Posts controller (empty for now) Make HTMX Play Nice with Rails HTMX needs to send CSRF tokens with requests or Rails will reject them. Add this to app/javascript/application.js: import "htmx.org" document.addEventListener('DOMContentLoaded', () => { const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); document.body.addEventListener('htmx:configRequest', (event) => { event.detail.headers['X-CSRF-Token'] = token; }); }); Want to save bandwidth? Use a CDN instead to serve the HTMX lib. In config/importmap.rb, replace its pin with: pin "htmx.org", to: "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js" Configure Routes Keeping it minimal. Edit config/routes.rb: root "home#index" resources :posts, only: [:create] We only need two routes: Homepage (root) Create posts endpoint Seed Some Data Let's add some fake posts. In db/seeds.rb: Post.create!(title: "First Post") Post.create!(title: "Hello, SQLite!") Post.create!(title: "HTMX + Rails 8 + SQLite Demo") Then run: rails db:seed Home Controller: Keep It Simple Update app/controllers/home_controller.rb: class HomeController This does several things: Shows a heading Creates a form that sends data to /posts with HTMX Tells HTMX to add the response to the end of #post-list Lists all existing posts Uses Tailwind classes to validate the CSS builds as expected via foreman The Post Partial Create app/views/posts/_post.html.erb: Just a list item with the post title. Start Your App Run this to start everything (it'll leverage Foreman behind the scenes): bin/dev Now visit http://localhost:3000 and try adding a post! Bonus: Rubymine Setup Run/Debug Configuration Click the vertical 3 dots near the "Debug/Run" buttons Select "Configuration > Edit..." Click "+", select "Gem Command" Set it up: Name: "Foreman" (or whatever) Gem name: foreman Executable name: foreman Arguments: start -f Procfile.dev Database Setup Double-click storage/development.sqlite3 in the project tree This opens the "Data Sources and Drivers" dialog Default options should work fine - install driver if needed Find your data under development > main > tables > posts And done! You now have a clean and fresh codebase to start your venture into Rails 8 with no Node/NPM/Yarn/Webpacker dependency - neither during developement, build steps, nor at runtime in production. Enjoy!

This guide is for developers who want to build something quickly without wading through paragraphs of theory or watching lengthy tutorial videos.
What We're Building
A simple app that:
- Uses Rails 8 and TailwindCSS 4
- Has HTMX for interactivity (Turbo and Stimulus are awesome though)
- SQLite3 for storage, but also Solid Cable, Solid Cache, and Solid Queue
- Lets you add posts without page refreshes (like it's 2025 or something)
- Has no Node / NPM dependency whatsoever, at any step
Setting up the project
First, let's create a new Rails app. I'm assuming you have RVM / a recent Ruby version installed and properly set up.
rails new my_rails_app --skip-javascript
cd my_rails_app/
Update your Gemfile
Open your Gemfile and add these gems:
# Add to the main group
gem "tailwindcss-rails"
gem "importmap-rails"
# Add to the development and test groups
group :development, :test do
# ... your existing development gems
gem "foreman"
end
These gems serve specific purposes:
- Tailwind: Provides utility classes to avoid writing custom CSS
- Importmap: Enables JS module usage without complex build steps
- Foreman: Manages multiple processes in a single terminal
Install All The Things
Time to install gems and set things up:
bundle install
rails tailwindcss:install
rails importmap:install
bin/importmap pin htmx.org@2.0.4
rails generate controller Home index
rails generate model Post title:string
rails db:migrate db:seed
rails generate controller Posts
This does a bunch of stuff in one go:
- Installs our gems
- Sets up Tailwind and importmap
- Adds HTMX to our app
- Creates a Home controller with an index action
- Makes a Post model with a title field
- Creates and migrates the database
- Generates a Posts controller (empty for now)
Make HTMX Play Nice with Rails
HTMX needs to send CSRF tokens with requests or Rails will reject them. Add this to app/javascript/application.js
:
import "htmx.org"
document.addEventListener('DOMContentLoaded', () => {
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRF-Token'] = token;
});
});
Want to save bandwidth? Use a CDN instead to serve the HTMX lib. In config/importmap.rb
, replace its pin with:
pin "htmx.org", to: "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"
Configure Routes
Keeping it minimal. Edit config/routes.rb
:
root "home#index"
resources :posts, only: [:create]
We only need two routes:
- Homepage (root)
- Create posts endpoint
Seed Some Data
Let's add some fake posts. In db/seeds.rb
:
Post.create!(title: "First Post")
Post.create!(title: "Hello, SQLite!")
Post.create!(title: "HTMX + Rails 8 + SQLite Demo")
Then run:
rails db:seed
Home Controller: Keep It Simple
Update app/controllers/home_controller.rb
:
class HomeController < ApplicationController
def index
@posts = Post.all
end
end
Simple and direct - just retrieve all posts.
Posts Controller: Even Simpler
Update app/controllers/posts_controller.rb
:
class PostsController < ApplicationController
def create
@post = Post.create!(title: params[:title])
render partial: "posts/post", locals: { post: @post }
end
end
This creates a post and returns just the HTML for the new post. HTMX will insert it into the page.
The Homepage View
Replace app/views/home/index.html.erb
with:
class="text-center">
class="text-4xl font-bold text-blue-500 mb-4">SQLite3 + HTMX Demo
id="post-list" class="mt-4 text-lg text-gray-700">
<% @posts.each do |post| %>
<%= render partial: "posts/post", locals: { post: post } %>
<% end %>
This does several things:
- Shows a heading
- Creates a form that sends data to
/posts
with HTMX - Tells HTMX to add the response to the end of
#post-list
- Lists all existing posts
- Uses Tailwind classes to validate the CSS builds as expected via foreman
The Post Partial
Create app/views/posts/_post.html.erb
:
class="p-2 border-b border-gray-300"><%= post.title %>
Just a list item with the post title.
Start Your App
Run this to start everything (it'll leverage Foreman behind the scenes):
bin/dev
Now visit http://localhost:3000 and try adding a post!
Bonus: Rubymine Setup
Run/Debug Configuration
- Click the vertical 3 dots near the "Debug/Run" buttons
- Select "Configuration > Edit..."
- Click "+", select "Gem Command"
- Set it up:
- Name: "Foreman" (or whatever)
- Gem name:
foreman
- Executable name:
foreman
- Arguments:
start -f Procfile.dev
Database Setup
- Double-click
storage/development.sqlite3
in the project tree - This opens the "Data Sources and Drivers" dialog
- Default options should work fine - install driver if needed
- Find your data under
development > main > tables > posts
And done!
You now have a clean and fresh codebase to start your venture into Rails 8 with no Node/NPM/Yarn/Webpacker dependency - neither during developement, build steps, nor at runtime in production. Enjoy!