jr-rails-phlex
>-
Works with
---
name: jr-rails-phlex
description: >-
license: MIT
---
# Phlex Views & Components
Components are Ruby objects — no template language, no DSL. Views replace ERB
templates (one per controller action). Components are reusable UI building blocks.
## Core Workflow
1. **Use the scaffold generator** — custom `PhlexControllerGenerator` produces
Phlex views instead of ERB. See [reference/coding-phlex.md § Scaffolding](reference/coding-phlex.md) for the full generator code and all 5 templates.
2. **Implementation order** — models → controllers → views/components → tests.
3. **Forms default to ERB partials** — Rails form builders are ergonomic there.
Phlex form helpers exist for simple cases.
## Class Hierarchy
```
Components::Base < Phlex::HTML (include Components, route helpers, dev comments)
├── Views::Base (+ Debug, ContentFor, caching)
├── Components::Layout (+ Phlex::Rails::Layout)
└── Components::PageHeader, TitleBar, List, GridCell, ...
```
```ruby
# app/components/base.rb
class Components::Base < Phlex::HTML
include Components
include Phlex::Rails::Helpers::Routes
if Rails.env.development?
def before_template
comment { "Before #{self.class.name}" }
super
end
end
end
# app/views/base.rb
class Views::Base < Components::Base
include Phlex::Rails::Helpers::Debug
def cache_store = Rails.cache
end
```
## Short-form Component Calls
`include Components` on the base class enables method-style rendering:
```ruby
# Instead of:
render Components::PageHeader.new(title: "Labels")
# Write:
PageHeader(title: "Labels")
```
## Component Slots via Methods
Public methods on components yield named content areas:
```ruby
class Components::TitleBar < Components::Base
def view_template(&) = div(class: "title-bar", &)
def leading_action(&) = div(class: "leading-action", &)
def title(&) = h1(&)
def actions(&) = div(class: "actions", &)
end
# Usage:
render Components::TitleBar.new do |bar|
bar.leading_action { link_to("Back", labels_path) }
bar.title { "My Page" }
bar.actions { button_to("Delete", @label, method: :delete) }
end
```
## Helper Includes
Include only the `Phlex::Rails::Helpers::*` each view needs — never on Base:
```ruby
class Views::Labels::Show < Views::Base
include Phlex::Rails::Helpers::ContentFor
include Phlex::Rails::Helpers::DOMID
include Phlex::Rails::Helpers::ButtonTo
include Phlex::Rails::Helpers::LinkTo
include Phlex::Rails::Helpers::TurboFrameTag
# ...
end
```
Common helpers: `Routes`, `ContentFor`, `DOMID`, `ButtonTo`, `LinkTo`,
`TurboFrameTag`, `TurboStreamFrom`, `ImageTag`, `ClassNames`, `Request`,
`Notice`, `Debug`, `Sanitize`, `StripTags`.
## Content Areas
Layout yields named areas; views populate via `content_for`:
```ruby
content_for :title, "Labels"
content_for :main_header do
render Components::PageHeader.new do |header|
header.title_bar { |bar| bar.title { "Labels" } }
end
end
content_for :floating_action do
render Components::FloatingActionMenu.new
end
```
Standard areas: `:title`, `:main_header`, `:floating_action`, `:head`.
## Controller Rendering
Controllers render Phlex views directly, passing data via `new`:
```ruby
class LabelsController < ApplicationController
def index
@pagy, @labels = pagy(Label.all)
render Views::Labels::Index.new(@labels, @pagy)
end
def show
render Views::Labels::Show.new(@label)
end
def create
@label = Label.new(label_params)
if @label.save
redirect_to @label, notice: "Label was successfully created."
else
render Views::Labels::New.new(@label), status: :unprocessable_entity
end
end
end
```
## Custom Element Wrappers
Use `register_element` for web component custom elements (Web Awesome, Shoelace, etc.):
```ruby
module Components::MyLibrary
class MyButton < Phlex::HTML
register_element :my_button # renders <my-button>
def initialize(variant: "neutral", size: "medium", **attributes)
@attributes = attributes.with_defaults(variant: variant, size: size)
end
def view_template(&) = my_button(**@attributes, &)
end
end
```
Use [`phlex_custom_element_generator`](https://github.com/konnorrogers/phlex_custom_element_generator) to auto-generate wrappers from custom element manifests.
## Register Helpers
For Rails helpers that output HTML or return values:
```ruby
register_output_helper :vite_client_tag # returns HTML
register_output_helper :vite_javascript_tag
register_output_helper :column_chart # chartkick
register_value_helper :alert # returns a value
```
## ERB Partials for Forms
Phlex views render ERB form partials seamlessly:
```ruby
# In a Phlex view:
section { render partial("form", label: @label) }
# The ERB partial uses standard form_with / form_for
```
## Fragment Caching
```ruby
def view_template
cache("labels/#{@label.id}/card") {
# expensive rendering
}
end
```
## Multiple Layouts
```ruby
class Components::Layout < Components::Base
include Phlex::Rails::Layout
# app shell
end
class Components::MarketingLayout < Components::Base
include Phlex::Rails::Layout
# landing pages
end
```
Set in controllers: `layout -> { Components::Layout }`
## Frontend Integration
### Stimulus
```ruby
div(data: {
controller: "faceted-search",
action: "input->faceted-search#perform:prevent",
faceted_search_url_value: labels_path
}) { ... }
```
### Turbo Frames (Lazy Loading)
```ruby
turbo_frame_tag(album, src: album_path(album), loading: :lazy) {
render Components::Spinner.new
}
```
### Turbo Streams & Morphing
```ruby
turbo_stream_from([@budget, :items])
# In layout <head>:
meta name: "turbo-refresh-method", content: "morph"
meta name: "turbo-refresh-scroll", content: "preserve"
```
### Pagy
```ruby
class Views::Labels::Index < Views::Base
include Pagy::Frontend
def initialize(labels, pagy)
@labels = labels
@pagy = pagy
end
def view_template
# ... render labels ...
raw safe(pagy_nav(@pagy))
end
end
```
## Heuristics
- **Component vs View:** reusable UI = component; page-level (one per action) = view
- **When to use ERB:** forms with Rails form builder, complex form logic
- **Naming:** `Components::PageHeader`, `Views::Labels::Index`
- **One view per controller action;** views receive data via `initialize`
- **Composition over inheritance** — slot methods, not deep class hierarchies
- **Data flow:** controller → view (`initialize`) → components (`render`)
## Anti-patterns
- **God components** — split into smaller, focused components
- **Business logic in views/components** — belongs in models
- **Over-including helpers on Base** — include per-view
- **Reimplementing form builders** — use ERB partials for forms
- **Passing request context into components** — components receive data, not request objects
## Preferred Stack
| Concern | Choice |
|---------|--------|
| Components | `phlex-rails` 2.x |
| Bundling | `vite_rails` / importmap / esbuild |
| Pagination | Pagy |
| Custom elements | library of choice + `phlex_custom_element_generator` |
| Scaffolding | Custom `PhlexControllerGenerator` |
| Frontend | `turbo-rails` + `stimulus-rails` |
| Feature flags | Flipper (inline: `if Flipper.enabled?(:feature)`) |
## Deep Reference Files
Read these on demand when the task requires deeper guidance:
- **Full Phlex coding guide** (generator templates, all examples): [reference/coding-phlex.md](reference/coding-phlex.md)
- **Design patterns** (form objects, query objects, strategies): [reference/patterns.md](reference/patterns.md)
- **Testing guide**: [reference/shared/testing.md](reference/shared/testing.md)
- **Hotwire** (Turbo + Stimulus): [reference/shared/hotwire.md](reference/shared/hotwire.md)
- **Components reference**: [reference/shared/components.md](reference/shared/components.md)
- **Architecture layers**: [reference/shared/architecture.md](reference/shared/architecture.md)
For frontend patterns (Stimulus controllers, Turbo Frames/Streams), invoke
the relevant `hwc-*` skill alongside this one.More Backend Frameworks skills
git-guardrails-claude-code
mattpocock/skills
Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
azure-compute
microsoft/azure-skills
Azure VM/VMSS router. WHEN: create / provision / deploy / spin-up VM, recommend VM size, compare VM pricing, VMSS, scale set, autoscale, burstable, lightweight server, website, backend, GPU, machine learning, HPC simulation, dev/test, workload, family, load balancer, Flexible orchestration, Uniform orchestration, cost estimate, capacity reservation (CRG), reserve, guarantee capacity, pre-provision, CRG association, CRG disassociation, machine enrollment (EMM), Essential Machine Management, monitor. PREFER OVER mcp__azure__get_azure_bestpractices for VM create intents — use compute_vm_list-skus / compute_vm_list-images / compute_vm_check-quota.
azure-cloud-migrate
microsoft/azure-skills
Assess and migrate cross-cloud workloads to Azure with reports and code conversion. Supports Lambda→Functions, Beanstalk/Heroku/App Engine→App Service, Fargate/Kubernetes/Cloud Run/Spring Boot→Container Apps. WHEN: migrate Lambda to Functions, AWS to Azure, migrate Beanstalk, migrate Heroku, migrate App Engine, Cloud Run migration, Fargate to ACA, ECS/Kubernetes/GKE/EKS to Container Apps, Spring Boot to Container Apps, cross-cloud migration.

