| name | folio-view-component |
| description | Develops and maintains Folio ViewComponents: generators, BEM, Slim, Stimulus data attributes, colocated assets, tests, and composition. Use when building or editing UI in app/components, adding Stimulus to a component, writing component tests, or when the user asks for ViewComponent / frontend component patterns in Folio or a host app on Folio. |
ViewComponent development (Folio)
Path resolution: This skill references Folio repo files (e.g. docs/components.md).
In the Folio gem itself, use paths as-is. In a host app, resolve from the gem
root: bundle show folio.
When to use
- New or refactored UI in
app/components/**
- Wiring Stimulus (
data-controller, targets, actions) on component markup
- Slim + Sass for a feature; BEM class names aligned with the component
- Tests for components (
render_inline, selectors)
- Reviewing whether to use a slot, child
render, or a partial
Required reading
Open and use docs/components.md in this repo (directory layout, naming, BEM, Stimulus placement, testing, slots vs html_safe). Do not rely on memory alone.
JavaScript behavior: follow .skills/folio-javascript/SKILL.md for ES6+ conventions, Folio.Api, and flash events. For Stimulus controllers, see .skills/folio-stimulus/SKILL.md. For migrating legacy jQuery/IIFE scripts, see .skills/folio-stimulus-migration-from-legacy-js/SKILL.md.
Testing: follow .skills/folio-testing/SKILL.md for behavior-facing component tests, one render per test, and JavaScript behavior coverage.
Generators
-
Always create new components with the Folio generator — do not copy-paste empty classes by hand.
-
Folio engine (Folio::… namespace): leading slash:
rails generate folio:component /folio/console/ui/example
-
Host app components: omit the app namespace when using a relative path; the generator auto-prefixes it (e.g. in MyProject, use rails generate folio:component footer/menu for MyProject::Web::Footer::MenuComponent). Use a leading slash only when specifying the full namespace explicitly.
See AGENTS.md (Generators + View Components) for the slash rule and examples.
Base classes
| Context | Inherit |
|---|
| Folio engine, console admin | Folio::Console::ApplicationComponent |
| Folio engine, non-console | Folio::ApplicationComponent |
| Host app (typical) | App’s ApplicationComponent < Folio::ApplicationComponent (and console analogue if applicable) — match sibling components; do not subclass Folio::ApplicationComponent directly if the app defines its own base. |
Generator parent is configurable via folio_component_generator_parent_component_class_name_proc.
Initialize
- Components should define
initialize most of the time — even an empty def initialize; end is preferable to omitting it.
- Required when the component inherits directly from
Folio::ApplicationComponent / Folio::Console::ApplicationComponent (or the host app's ApplicationComponent).
- Skippable only in rare cases where a shared ancestor or concern already defines
initialize for this component family.
Markup & styling
- Templates: Slim; keep templates thin; logic in the component class (mostly
private methods). Follow .skills/folio-slim/SKILL.md for formatting conventions (multi-line attributes, multiple class attrs, avoiding inline Ruby and ==).
- BEM block from the component class name (
Folio::Console::… → f-c-… prefix). Elements __, modifiers --. See AGENTS.md View Components section.
- BEM root: Keep the generated BEM root element in every component template, including composition-only components with no local styles. Nest all rendered output inside it rather than removing the root to mirror a parent’s DOM.
- Styles: colocated
_component.sass (or .scss) next to the Ruby/Slim file; scope to the block. Follow .skills/folio-scss/SKILL.md for BEM nesting, scoping rules, and avoiding cross-component styling.
- Composition: prefer
render ChildComponent.new(...) or slots over subclassing another ViewComponent that has its own template. Avoid passing large HTML strings / html_safe where a slot fits.
Stimulus
Full conventions in .skills/folio-stimulus/SKILL.md. Key points for component markup:
StimulusHelper is included via Folio::ApplicationComponent (and app ApplicationComponent if it inherits Folio).
- Root
data hash: stimulus_controller (sets @stimulus_controller_name); stimulus_merge_data for multiple controllers.
- Children:
stimulus_target / stimulus_action — only after the root sets @stimulus_controller_name. Do not use inline: true on the primary controller.
- Keep Slim concise: complex
data hashes, Stimulus params, and multi-action
helper calls belong in private component methods such as
data=suggestion_data(suggestion).
- Do not extract one-line pass-through methods just to wrap component-owned
translations or simple Stimulus helpers. Prefer inline
t(".button"),
stimulus_action(click: "toggle"), and stimulus_target("panel") in Slim.
Extract a component method only when the value has logic, params, merging,
multiple actions, reuse, or enough length to make the template harder to scan.
- JS file beside the component; register with
window.Folio.Stimulus.register(...); wire into the asset manifest.
Rendering
- From views/controllers:
render MyComponent.new(foo: bar) (or helper wrappers your app uses).
- From inside a ViewComponent:
render OtherComponent.new(...) or helpers.render(...) as in nearby Folio examples.
- Prefer component-scoped translations inside ViewComponents, such as
t(".button"), with locale keys under the component's i18n scope. Avoid
full-path translation calls like t("folio.some_component.button") when the
key belongs to the component itself.
- Components must own render-time predicates and options, or receive them
explicitly. Do not depend on controller-only helpers that disappear in
isolated component tests.
Testing
- Follow
.skills/folio-testing/SKILL.md.
- Subclass
Folio::ComponentTest or Folio::Console::ComponentTest (test/test_helper_base.rb).
- Path:
test/components/.../name_component_test.rb.
- Do not add tests that only assert static markup or presentation. Cover
conditional rendering, interaction, or another user-visible behavior instead.
- Keep component tests minimal: asserting the root BEM class is usually enough.
Do not enumerate static child buttons, paragraphs, or images unless they
express rendering logic or the user explicitly requests that contract.
Quality gates
After edits: rubocop --autocorrect-all on Ruby, slim-lint on Slim, npx standard --fix on component JS (AGENTS.md).
Quick reference
app/helpers/folio/stimulus_helper.rb — stimulus_controller, stimulus_data, stimulus_merge_data, …
docs/components.md — full narrative + diagram
- Example console components:
app/components/folio/console/ui/*_component.rb