rails-inertia-stack
Generate production-ready Rails applications with Inertia.js, React, TypeScript, Server-Side Rendering (SSR), and ShadcnUI components, configured for deployment with Kamal. Use when creating new Rails projects that need modern SPA-like frontend with SEO-friendly SSR, or when helping users set up Inertia.js with Rails. Supports PostgreSQL, MySQL, and SQLite databases.
Best use case
rails-inertia-stack is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Generate production-ready Rails applications with Inertia.js, React, TypeScript, Server-Side Rendering (SSR), and ShadcnUI components, configured for deployment with Kamal. Use when creating new Rails projects that need modern SPA-like frontend with SEO-friendly SSR, or when helping users set up Inertia.js with Rails. Supports PostgreSQL, MySQL, and SQLite databases.
Teams using rails-inertia-stack should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/rails-inertia-stack/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How rails-inertia-stack Compares
| Feature / Agent | rails-inertia-stack | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Generate production-ready Rails applications with Inertia.js, React, TypeScript, Server-Side Rendering (SSR), and ShadcnUI components, configured for deployment with Kamal. Use when creating new Rails projects that need modern SPA-like frontend with SEO-friendly SSR, or when helping users set up Inertia.js with Rails. Supports PostgreSQL, MySQL, and SQLite databases.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Rails Inertia Stack Generator
## Overview
Generate complete, production-ready Rails applications with Inertia.js for SPA-like user experience, React + TypeScript frontend, Server-Side Rendering for SEO, ShadcnUI component library, and Kamal deployment configuration. The skill handles the entire setup process including Dockerfile configuration for SSR, database accessories, and deployment configuration.
## When to Use This Skill
Use this skill when:
- Creating new Rails projects with modern frontend stack
- Setting up Inertia.js with React and TypeScript in Rails
- Configuring Server-Side Rendering for Rails + Inertia applications
- Setting up ShadcnUI with Rails projects
- Preparing Rails applications for Kamal deployment
- Migrating between databases (PostgreSQL/MySQL/SQLite)
## Core Workflow
### Step 1: Determine Project Requirements
Ask the user for project configuration:
- **Project name** (required)
- **Database choice**: PostgreSQL (recommended for production), MySQL, or SQLite
- **Server IP addresses** for deployment (can be configured later)
- **Domain name** for SSL (optional, can be configured later)
### Step 2: Create Rails Project
Execute the appropriate Rails new command based on database choice:
**PostgreSQL:**
```bash
rails new PROJECT_NAME -d postgresql --skip-javascript
cd PROJECT_NAME
```
**MySQL:**
```bash
rails new PROJECT_NAME -d mysql --skip-javascript
cd PROJECT_NAME
```
**SQLite:**
```bash
# Default with Solid stack
rails new PROJECT_NAME --skip-javascript
cd PROJECT_NAME
# Or without Solid stack (if user requests)
rails new PROJECT_NAME --skip-javascript --skip-solid
cd PROJECT_NAME
```
### Step 3: Install Inertia Rails Stack
Run these commands in sequence:
```bash
# Add Inertia Rails gem
bundle add inertia_rails
# Install frontend stack (non-interactive)
bin/rails generate inertia:install \
--framework=react \
--typescript \
--vite \
--tailwind \
--no-interactive
```
**When prompted about bin/dev conflict:** Choose `Y` to overwrite.
After installation:
```bash
# Setup databases
bin/rails db:setup
bin/rails db:migrate
```
### Step 4: Configure RuboCop (Optional but Recommended)
Rails 8 includes RuboCop Rails Omakase by default. Create or update `.rubocop.yml`:
```yaml
# Omakase Ruby styling for Rails
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
# Overwrite or add rules to create your own house style
#
# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
# Layout/SpaceInsideArrayLiteralBrackets:
# Enabled: false
# Restore strict 2-space indentation enforcement
Layout/IndentationConsistency:
Enabled: true
Layout/IndentationWidth:
Enabled: true
Width: 2
```
**Why this matters:**
- Maintains consistent code style across the project
- Omakase provides sensible Rails defaults
- Strict 2-space indentation ensures readability
### Step 5: Fix Development Configuration
Apply two critical fixes for development:
**Fix 1: Procfile.dev** (ensure Rails runs on port 3000)
Read `Procfile.dev` and swap the order:
```
web: bin/rails s
vite: bin/vite dev
```
**Fix 2: config/vite.json** (enable 127.0.0.1 access)
Add `"host": "127.0.0.1"` to development section:
```json
{
"development": {
"autoBuild": true,
"publicOutputDir": "vite-dev",
"port": 3036,
"host": "127.0.0.1"
}
}
```
### Step 6: Setup ShadcnUI
Configure TypeScript for ShadcnUI (CRITICAL: update BOTH files):
**tsconfig.app.json** - Add to compilerOptions:
```json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/frontend/*"]
}
}
}
```
**tsconfig.json** - Add to root:
```json
{
"compilerOptions": {
"baseUrl": "./app/frontend",
"paths": {
"@/*": ["./*"]
}
}
}
```
**Initialize ShadcnUI (non-interactive):**
```bash
npx shadcn@latest init --defaults --yes
npx shadcn@latest add button --yes --overwrite
```
### Step 7: Configure Server-Side Rendering (SSR)
**Create SSR entry point** at `app/frontend/ssr/ssr.tsx`:
```typescript
import { createInertiaApp } from '@inertiajs/react'
import createServer from '@inertiajs/react/server'
import ReactDOMServer from 'react-dom/server'
createServer((page) =>
createInertiaApp({
page,
render: ReactDOMServer.renderToString,
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.tsx', { eager: true })
return pages[`../pages/${name}.tsx`]
},
setup: ({ App, props }) => <App {...props} />,
}),
)
```
**Enable client-side hydration** in `app/frontend/entrypoints/inertia.ts`:
Add `hydrateRoot` import:
```typescript
import { createRoot, hydrateRoot } from 'react-dom/client'
```
Update setup function:
```typescript
setup({ el, App, props }) {
if (el) {
if (import.meta.env.MODE === "production") {
hydrateRoot(el, createElement(App, props))
} else {
createRoot(el).render(createElement(App, props))
}
} else {
console.error('Missing root element...')
}
}
```
**Enable SSR in Vite** - Add to `config/vite.json`:
```json
{
"production": {
"ssrBuildEnabled": true
}
}
```
**Enable SSR in Inertia Rails** - Update `config/initializers/inertia_rails.rb`:
```ruby
InertiaRails.configure do |config|
config.version = ViteRuby.digest
config.encrypt_history = true
config.always_include_errors_hash = true
config.ssr_enabled = ViteRuby.config.ssr_build_enabled
end
```
### Step 8: Configure Dockerfile for SSR
TWO modifications needed to the generated Dockerfile!
Reference `references/dockerfile-ssr-patterns.md` for complete examples.
**Modification 1: Install Node.js in BASE stage**
Add AFTER "Install base packages", BEFORE bundler installation:
```dockerfile
# Install JavaScript runtime (prebuilt Node per-arch)
ARG NODE_VERSION=25.0.0
ARG TARGETARCH
ENV PATH=/usr/local/node/bin:$PATH
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y xz-utils && \
case "${TARGETARCH}" in \
amd64) NODEARCH=x64 ;; \
arm64) NODEARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac && \
mkdir -p /usr/local/node && \
curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODEARCH}.tar.xz" | \
tar -xJ -C /usr/local/node --strip-components=1 && \
/usr/local/node/bin/node -v && \
/usr/local/node/bin/npm -v && \
apt-get purge -y --auto-remove xz-utils && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
```
**Modification 2: Install Bundler with Locked Version**
Add AFTER Node.js installation, BEFORE "Set production environment":
```dockerfile
# Ensure the Bundler version matches Gemfile.lock to avoid per-build upgrades.
RUN gem install bundler -v 2.7.2 -N
```
**Important:** Check `Gemfile.lock` bottom section for `BUNDLED WITH` version and update to match:
```bash
tail Gemfile.lock
# BUNDLED WITH
# 2.7.2
```
**That's all!** No changes to BUILD stage (Vite Ruby handles npm install), no changes to final stage, no EXPOSE, no CMD modifications.
**Database client variations** - adjust base packages only:
- PostgreSQL: `postgresql-client` (base) and `libpq-dev` (build)
- MySQL: `default-mysql-client` (base) and `libmysqlclient-dev` (build)
- SQLite: No database client needed
### Step 9: Configure Kamal Deployment
Update `config/deploy.yml` with SSR and database configuration.
Reference `references/kamal-ssr-deployment.md` and `references/complete-guide.md` for detailed examples.
**Key additions:**
1. Add `vite` server with `init: true` and `network-alias: vite_ssr` options
2. Add `INERTIA_SSR_URL: http://vite_ssr:13714` to env
3. Configure database accessory (PostgreSQL/MySQL) or volumes (SQLite)
4. Update database environment variables
5. (Optional) Add Redis/Valkey accessory if needed for caching, queues, or Action Cable
**Redis/Valkey Configuration (if requested):**
Use Valkey instead of Redis due to licensing concerns:
```yaml
accessories:
redis:
image: valkey/valkey:9 # Use Valkey, not redis image
host: SERVER_IP
port: "127.0.0.1:6379:6379"
directories:
- redis_data:/data
env:
clear:
REDIS_URL: redis://PROJECT_NAME-redis:6379/1
```
**Solid Queue Configuration:**
- If user created project WITH Solid (default), include:
```yaml
env:
clear:
SOLID_QUEUE_IN_PUMA: true
```
- If user created project with `--skip-solid`, OMIT this variable
**Optional: Async Job Server (Advanced):**
For dedicated job processing (alternative to `SOLID_QUEUE_IN_PUMA`), add a job server:
```yaml
servers:
job:
hosts:
- SERVER_IP
cmd: bundle exec async-job-adapter-active_job-server
```
This runs jobs in a separate container for better resource isolation.
**Create database initialization file** at `db/production.sql`:
```sql
CREATE DATABASE PROJECT_NAME_production_cache;
CREATE DATABASE PROJECT_NAME_production_queue;
CREATE DATABASE PROJECT_NAME_production_cable;
```
**Update database.yml** production section:
```yaml
production:
primary: &primary_production
<<: *default
host: <%= ENV["DB_HOST"] %>
database: PROJECT_NAME_production
username: PROJECT_NAME
password: <%= ENV["POSTGRES_PASSWORD"] %> # or MYSQL_ROOT_PASSWORD
```
### Step 10: Test SSR Build
Verify the setup works:
```bash
export RAILS_ENV=production
./bin/rails assets:precompile
```
Check for successful output:
- Client bundle build completes
- SSR bundle build completes
- `public/vite-ssr/ssr.js` exists
### Step 11: Provide Deployment Instructions
Inform the user about deployment configuration:
1. **Configure secrets** in `.env` (git-ignored):
```bash
POSTGRES_PASSWORD=secure-password
KAMAL_REGISTRY_PASSWORD=docker-hub-token
```
2. **Update `.kamal/secrets`**:
```bash
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$(cat config/master.key)
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
```
3. **Update `config/deploy.yml`** with actual server IPs and domain
4. **Deploy**:
```bash
export $(grep -v '^#' .env | xargs)
git add . && git commit -m "Setup Inertia Rails with SSR"
kamal setup
kamal deploy
```
## Critical Patterns
### Non-Interactive Installation
Always use these flags to avoid prompts:
```bash
# ShadcnUI initialization
npx shadcn@latest init --defaults --yes
npx shadcn@latest add COMPONENT --yes --overwrite
# Inertia installation
bin/rails generate inertia:install --no-interactive
```
### SSR Build Process
- ✅ Use `rails assets:precompile` (handles both client and SSR, plus npm install)
- ❌ Do NOT run `bin/vite build --ssr` separately (redundant)
- ❌ Do NOT run `npm ci` separately (Vite Ruby handles it)
### Dockerfile Node.js
- ✅ Install Node.js in base stage using prebuilt binaries (supports multi-arch)
- ✅ Node.js automatically included in final stage
- ❌ Do NOT remove Node.js after build (breaks SSR)
- ❌ Do NOT run npm ci separately (Vite Ruby handles it)
### Kamal SSR Architecture
```
Web Container (Rails) ←→ vite_ssr Container (Node.js)
via http://vite_ssr:13714
```
Fixed hostname via `network-alias: vite_ssr` enables reliable connection.
## Database-Specific Variations
### PostgreSQL (Production Recommended)
- **Gem:** `pg`
- **Dockerfile client:** `postgresql-client`
- **Dockerfile build lib:** `libpq-dev`
- **Accessory image:** `postgres:18` (latest stable)
- **Volume mount:** `data:/var/lib/postgresql` (BREAKING CHANGE in PostgreSQL 18: previous versions used `data:/var/lib/postgresql/data`)
- **Port:** `127.0.0.1:5432:5432`
- **Env vars:** `POSTGRES_USER`, `POSTGRES_DB`, `POSTGRES_PASSWORD`
### MySQL
- **Gem:** `mysql2`
- **Dockerfile client:** `default-mysql-client`
- **Dockerfile build lib:** `libmysqlclient-dev`
- **Accessory image:** `mysql:9.4.0` (latest Innovation) or `mysql:8.4` (LTS recommended for production)
- **Port:** `127.0.0.1:3306:3306`
- **Env vars:** `MYSQL_ROOT_HOST`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_ROOT_PASSWORD`
### SQLite (Development/Small Apps)
- **Gem:** `sqlite3` (default)
- **Dockerfile:** No database client needed
- **Deployment:** Use volumes instead of accessory
- **Important:** SQLite databases are stored in `storage/` directory in Rails 8+
- **Volume configuration:**
```yaml
# CORRECT: Mount storage directory (contains production.sqlite3)
volumes:
- "PROJECT_NAME_storage:/rails/storage"
# INCORRECT: Do not mount db directory
# volumes:
# - "PROJECT_NAME_db:/rails/db"
```
- **Database location:** Check `config/database.yml` production section for actual path:
```yaml
production:
<<: *default
database: storage/production.sqlite3 # Note: stored in storage/ not db/
```
## Common Pitfalls to Avoid
1. **Missing BOTH tsconfig updates** - ShadcnUI requires updating both `tsconfig.json` and `tsconfig.app.json`
2. **Interactive commands** - Always use `--defaults --yes` with shadcn, `--no-interactive` with inertia:install
3. **Removing Node.js from Dockerfile** - Node.js must remain in production for SSR
4. **Missing network-alias** - Rails cannot connect to SSR without `network-alias: vite_ssr`
5. **Wrong Procfile.dev order** - Vite first causes Rails to run on port 3100 instead of 3000
6. **Missing vite.json host** - Without `"host": "127.0.0.1"`, connections to 127.0.0.1 fail
7. **Not updating database.yml** - Must add `host: <%= ENV["DB_HOST"] %>` and password for production
8. **Wrong SQLite volume path** - Must mount `/rails/storage` (not `/rails/db`) since Rails 8 stores SQLite in storage/ directory
9. **Using Redis image instead of Valkey** - Use `valkey/valkey:9` image to avoid Redis licensing concerns
10. **Bundler version mismatch** - Must lock bundler version in Dockerfile to match `Gemfile.lock` to prevent cache invalidation
## Troubleshooting
### SSR Build Fails
- Check Node.js is in Dockerfile base stage
- Verify `config/vite.json` has `"ssrBuildEnabled": true`
- Ensure `app/frontend/ssr/ssr.tsx` exists
- Run `npm ci` to reinstall dependencies
### Rails Can't Connect to SSR
- Verify `vite` server has `network-alias: vite_ssr` in deploy.yml
- Check `INERTIA_SSR_URL: http://vite_ssr:13714` in env
- Ensure both web and vite containers are on same network
### Database Connection Fails
- Verify `DB_HOST` matches accessory service name (e.g., `PROJECT_NAME-db`)
- Check database accessory is running: `kamal accessory details db`
- Confirm password matches between `.kamal/secrets` and `config/database.yml`
### ShadcnUI Components Not Resolving
- Verify BOTH tsconfig files have path aliases
- Ensure imports use `@/` prefix: `import { Button } from '@/components/ui/button'`
## Resources
### references/
- `complete-guide.md` - Complete 778-line implementation guide with all variations
- `dockerfile-ssr-patterns.md` - Dockerfile modifications for PostgreSQL/MySQL/SQLite
- `kamal-ssr-deployment.md` - Kamal SSR architecture and deployment examples
### assets/
- `ssr-entry.tsx` - SSR server entry point template
- `deploy-postgres.yml` - PostgreSQL deployment configuration
- `deploy-mysql.yml` - MySQL deployment configuration
- `deploy-sqlite.yml` - SQLite deployment configuration
Use these references when detailed examples are needed or when helping users with specific database configurations.Related Skills
tanstack-start
TanStack Start full-stack React framework patterns. Use when working with file-based routing, API routes, server handlers, TanStack Router layouts, or integrating AI/MCP features in this codebase.
tanstack-query
TanStack Query (React Query) for asynchronous server-state management with automatic caching, background refetching, optimistic updates, and pagination in React applications.
tall-stack-general
General guidelines for TALL stack development, emphasizing Laravel and PHP best practices.
sw-tech-stack-planner
Use when user wants a tech stack recommendation, technology choices, docker-compose setup, or architecture decisions for a software project – reads vision.md, user-stories.md, use-cases.md and generates requirements/tech-stack.yaml silently.
stacktape
Build and deploy full-stack apps to AWS. Use when user wants to deploy an app, build a SaaS, create an API, set up a database, or ship any web application to production.
stack
Load technology stack configurations with project templates, dependencies, and best practices. Supports dotnet, node, python, java, go.
stack-selection
Tech stack selection with decision trees, user preferences, and blueprint defaults. Use when choosing frameworks, libraries, or tools for new projects or features.
sojustack-best-practices
Best-practice guidance for the SojuStack monorepo (NestJS + Drizzle + Better Auth + TanStack Start). Use when editing files in apps/api or apps/web, designing routes, query/form patterns, auth/transaction flows, or implementing cross-stack features.
setup-tanstack-start
Bootstrap a new web project with TanStack Start, React, Tailwind CSS v4, and shadcn/ui on top of the base tooling stack. Consult this skill whenever creating a web app, setting up a frontend project, starting a React application, or initializing anything involving TanStack Start, TanStack Router, TanStack Query, Tailwind, shadcn, or Vite.
senior-fullstack
Fullstack development toolkit with project scaffolding for Next.js/FastAPI/MERN/Django stacks and code quality analysis. Use when scaffolding new projects, analyzing codebase quality, or implementing fullstack architecture patterns.
senior-fullstack-ai-engineer
Senior full-stack developer with 10+ years of experience and AI engineering expertise. Builds production-ready applications using modern frameworks (Flask, FastAPI, React), AI/ML technologies (LLMs, RAG, model deployment), and cloud infrastructure. Use for all development tasks requiring full-stack and AI/ML implementation.
rails-jobs
Use when setting up background jobs, caching, or WebSockets - SolidQueue, SolidCache, SolidCable (TEAM RULE