Hotwire starter
Rails 8.1 server-rendered starter kit with Hotwire (Turbo + Stimulus), ViewComponent, Tailwind v4, and Supabase Auth via supabase-rails in :web mode with encrypted cookie sessions.
The Hotwire starter is a server-rendered Rails 8.1 monolith with Supabase Auth bolted in through supabase-rails in :web mode. Sign-in, sign-up, password reset, OTP / magic-link, and GitHub OAuth all run through the gem's controllers; the browser holds an encrypted sb-session cookie, not a token. Every request through the Rails app forwards that cookie to Supabase, the gem's middleware unwraps it into Current.user, and controllers render Turbo-powered HTML.
What is it
A Rails 8.1 monolith pre-wired with the pieces a Hotwire app needs the day it ships:
- Cookie-backed auth —
supabase-railsin:webmode mints an encryptedsb-sessioncookie (AES-GCM), refreshes the underlying Supabase access token transparently, and exposesCurrent.userplus a per-request RLS-scoped Supabase client to every controller. - Hotwire — Turbo Drive, Turbo Frames, Turbo Streams, and Stimulus are all wired into the layout via Importmap; no JavaScript build step.
- ViewComponent — UI primitives (
SidebarComponent,UserMenuComponent,ButtonComponent,AvatarComponent, etc.) underapp/components/. - Tailwind v4 + Railsblocks — Tailwind built via
tailwindcss-rails, Railsblocks components installed via Importmap pins and CDN CSS/JS. - Lucide icons —
lucide-railsprovides theiconhelper used across views and components. - Notes resource — a RLS-governed
notestable plus aNotesControllerthat demonstrates the per-request, cookie-overlaid Supabase client pattern. - Local Supabase — a checked-in
supabase/directory withconfig.tomland seed migrations boots a full Auth + Postgres stack viasupabase start. - System + E2E tests — Minitest, Capybara + headless Chrome, plus an end-to-end suite under
test/e2e/that runs against a real local Supabase stack.
Who it's for
You should pick this kit when:
- You want a productive server-rendered Rails app — controllers render HTML, Hotwire turns it into a snappy SPA-feeling experience, and you don't want to stand up a separate API tier.
- You want Supabase Auth to own the identity surface (sign-up, email confirmation, OAuth, password reset, magic-link) and let your Rails app focus on domain logic.
- You're comfortable with an encrypted cookie session rather than holding tokens in JavaScript. The browser never sees an access token.
- You'd rather use RLS in Postgres than re-encode authorisation in Pundit policies — every
Current.user.supabasecall inherits the signed-in user's row-level policies.
If your frontend is a mobile or third-party SPA, look at the Rails API starter — JWT-only, no cookies. If you want a typed React frontend in the same Rails process, look at the Inertia + React starter — same cookie-auth model, different render layer.
What's included
| Layer | What ships in the box |
|---|---|
| Auth | supabase-rails in :web mode, Authentication concern, expired-session flash, custom SessionsController#destroy that lands on /welcome |
| Auth UI | app/views/supabase/rails/{sessions,registrations,passwords,otp,oauth}/ view overrides on a Tailwind auth.html.erb layout |
| Routes | Public /welcome, public / (dashboard shell), authenticated /dashboard, /notes, /settings/profile, /settings/appearance |
| Components | SidebarComponent, UserMenuComponent, AvatarComponent, AppLogoComponent, ButtonComponent, SeparatorComponent, plus auth-specific components |
| Stimulus | app/javascript/controllers/ wired by eagerLoadControllersFrom; ships a placeholder hello_controller.js and a sidebar controller used by the application layout |
| Data | supabase/migrations/ boots a public.notes table with RLS policies; SQLite drives Rails app data plus Solid Cache/Queue/Cable |
| Tests | Minitest controllers + system tests, plus test/e2e/ running against supabase start |
| Deploy | Dockerfile and Kamal config/deploy.yml |
What's not included
The kit deliberately stops short of the post-v1 auth surface so you can grow into it when you need it:
- MFA (TOTP + backup codes), passkeys / WebAuthn, sudo mode, identity verification. All v2 work — the matching gems (
rotp,rqrcode,webauthn,bcrypt) are not in the Gemfile, and the settings sidebar has no security section. - An ActiveRecord
Usermodel.Current.useris a value object built from the Supabase session — no shadowuserstable, no sync drift. Opt into a host-app users table later withbin/rails generate supabase:user_model. - A JavaScript bundler. Importmap + Propshaft serve everything; there is no
package.json, nonode_modules, no Vite or esbuild. Add one if your needs outgrow Importmap. - Background jobs. Solid Queue is configured but unused — the kit ships no jobs.
- Per-target deploy guides.
Dockerfileand Kamal config are generic; turning them into a Fly/Render/Heroku flow is left to your platform. - Production observability. Logs go to STDOUT. Wire your APM (Sentry, Datadog, OpenTelemetry) when you need it.
Next
Getting started
Prerequisites, clone, install, env, and a signed-in dashboard in 15 minutes.
Project structure
Walk through app/, config/, supabase/, db/, and test/ at the depth a new contributor needs.
Architecture
How supabase-rails plugs into Turbo + Stimulus, the cookie session lifecycle, and how the supabase/ directory is used.
Customization
Hotwire-flavored recipes — Turbo Frame inline editing, a Stimulus controller wired to Supabase Realtime, a new ViewComponent.
Deployment
Production-readiness checklist — secrets, SQLite-or-Postgres, TLS, cache, health checks.
Troubleshooting
First-run failures and how to read them — missing env, expired-session loops, empty notes list, Tailwind not building.
Repository
- Source:
supabase-ruby/starter-kit-hotwire - Underlying gem:
supabase-rails
Troubleshooting
Most likely first-run failures with the Rails API starter — 401 loops, JWKS errors, CORS preflight, Postgres connection refused, missing env, swagger regeneration — and how to resolve them.
Getting started
Clone the Hotwire starter, install dependencies, point it at a Supabase project, and sign in for the first time.