create-view
Use this skill when creating Hanami 2.x Views.
Core principle: Views are objects, not template files. They encapsulate presentation logic and expose data to templates.
Quick Reference
| Scenario | Approach |
|---|---|
| Create a View | Class inherits from Hanami::View in app/views/ |
| Define exposures | Use expose :name to declare what the template receives |
| Expose with transformation | expose :user { |user| UserPresenter.new(user) } |
| Render a template | View automatically looks up templates/<path>.html.erb |
| Pass context | expose :current_user, as: :current_user |
| Define a layout | layout "application" in the View class |
| Set template format | format :html (default) or format :json |
| Access exposures in template | <%= user.name %> (locals passed to ERB) |
Core Rules
Create the View file in the app or slice:
# app/views/users/show.rb # frozen_string_literal: true module MyApp module Views module Users class Show < MyApp::View expose :user end end end endCreate the template alongside the View:
<!-- app/templates/users/show.html.erb --> <h1><%= user.name %></h1> <p><%= user.email %></p>Expose data from the Action:
def handle(request, response) user = user_repo.by_id(request.params[:id]).one response.render(view, user: user) endTransform exposures within the View:
class Show < MyApp::View expose :user do |user| { name: "#{user.first_name} #{user.last_name}", email: user.email, member_since: user.created_at.strftime("%B %Y") } end endUse Parts for decorator-style logic (
decorate-with-parts):class Show < MyApp::View expose :user, as: :user_part endDefine a layout:
class Show < MyApp::View layout "application" end<!-- app/templates/layouts/application.html.erb --> <!DOCTYPE html> <html> <head><title>MyApp</title></head> <body> <%= yield %> </body> </html>Keep Views focused on presentation. No database queries, no business logic. Views receive prepared data from Actions.
Use explicit exposures. Do not pass raw params or unparsed data to Views.
Common Mistakes & Red Flags
| Mistake / Red Flag | Reality |
|---|---|
| Database queries in View classes or templates | Views are for presentation only. All data fetching happens in Actions via Repositories. |
@instance_variables in templates |
Templates receive locals from expose. No instance variables. |
| Skipping the View class and rendering templates directly | Always define a View class. It encapsulates presentation logic and makes templates testable. |
| Template path mismatched from View namespace | Templates follow the View namespace: app/views/users/show.rb → app/templates/users/show.html.erb. |
Integration
| Related Skill | When to chain |
|---|---|
| create-action | Actions render Views and pass exposures. Master Action structure first. |
| decorate-with-parts | Use Parts for complex decorator-style logic in Views. |
| create-repository | Actions fetch data from Repositories before passing to Views. |
| write-request-spec (testing) | Test the full stack: request → Action → View → template. |