---
title: LLMs Best Practices - Rails + InertiaJS
url: https://colingoudie.me/llms-rails.txt
description: Rails with InertiaJS conventions for AI-assisted development
related:
- https://colingoudie.me/llms.txt
---
# LLMs Best Practices - Rails + InertiaJS
Include this in your CLAUDE.md or similar AI assistant configuration file.
See also: https://colingoudie.me/llms.txt
## InertiaJS Response Patterns
### Controller Responses
Always use render inertia: - never render json:
```ruby
# CORRECT - InertiaJS response
def show
@project = Project.find(params[:id])
render inertia: "Projects/Show", props: {
project: ProjectSerializer.new(@project).as_json
}
end
# WRONG - JSON response breaks Inertia
def show
@project = Project.find(params[:id])
render json: @project
end
```
### Redirects
```ruby
# Standard redirect (Inertia handles it)
redirect_to projects_path, notice: "Project created"
# With Inertia flash
redirect_to projects_path, inertia: { flash: { success: "Created!" } }
```
### Shared Data
```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
inertia_share do
{
current_user: current_user&.as_json(only: [:id, :name, :email]),
flash: flash.to_h
}
end
end
```
## Controller Conventions
### Thin Controllers
```ruby
# GOOD - delegates to service/model
class ProjectsController < ApplicationController
def create
result = Projects::CreateService.call(project_params, current_user)
if result.success?
redirect_to project_path(result.project), notice: "Created"
else
redirect_back fallback_location: projects_path, alert: result.error
end
end
end
```
### Strong Parameters
```ruby
private
def project_params
params.require(:project).permit(:name, :description, :status, tag_ids: [])
end
```
## Serializers
### Use Serializers for All Props
```ruby
# app/serializers/project_serializer.rb
class ProjectSerializer
def initialize(project)
@project = project
end
def as_json
{
id: @project.id,
name: @project.name,
description: @project.description,
status: @project.status,
created_at: @project.created_at.iso8601,
user: UserSerializer.new(@project.user).as_json
}
end
end
```
### Collection Serialization
```ruby
def index
@projects = Project.includes(:user).page(params[:page])
render inertia: "Projects/Index", props: {
projects: @projects.map { |p| ProjectSerializer.new(p).as_json },
pagination: pagination_meta(@projects)
}
end
```
## Multi-Tenancy (ActsAsTenant)
If using ActsAsTenant:
```ruby
# Always scope queries through tenant
class Project < ApplicationRecord
acts_as_tenant :organisation
end
# Never bypass tenant scoping
# WRONG: Project.unscoped.find(id)
# CORRECT: current_tenant.projects.find(id)
```
## Frontend Components (React/Vue)
### Page Components
```tsx
// Pages/Projects/Show.tsx
import { usePage } from "@inertiajs/react";
interface Props {
project: Project;
}
export default function Show({ project }: Props) {
return (
{project.name}