An object oriented view layer for Hanami. Write views, layouts, and components as Phlex classes instead of templates.
  • Ruby 97%
  • Shell 3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Aaron Allen 0d1284ea67
Some checks failed
Build / Lint (push) Failing after 3s
Build / Test (Ruby 3.3) (push) Failing after 3s
Build / Test (Ruby 3.4) (push) Failing after 2s
Build / Test (Ruby 4.0) (push) Failing after 2s
scripts: fall back to editorconfig-checker in the lint task
The editorconfig lint task called `ec` directly, but the mise release
on CI installs the binary as `editorconfig-checker` with no `ec`, so
the Lint job failed and blocked the 0.3.0 release.

The task now runs `ec` when it exists and `editorconfig-checker`
otherwise. It checks `ec` first because a laptop can hold a stale
`editorconfig-checker` shim from another version next to a working
`ec`, which is what the previous fix guarded against.
2026-10-07 10:51:28 -05:00
.claude release: prepare v0.2.0 release 2026-09-10 15:42:52 -05:00
.config config: allow a component initializer to skip super 2026-09-06 20:02:47 -05:00
.github release: prepare v0.2.0 release 2026-09-10 15:42:52 -05:00
docs/usage docs: document app routes and view_name_inference_base 2026-10-07 10:11:54 -05:00
lib lib: key views under the slice's view_name_inference_base 2026-10-07 10:11:53 -05:00
scripts scripts: fall back to editorconfig-checker in the lint task 2026-10-07 10:51:28 -05:00
sig sig: type Extensions::Slice.view_key 2026-10-07 10:11:54 -05:00
spec spec: cover slice routes and view_name_inference_base 2026-10-07 10:11:54 -05:00
.editorconfig Initial commit 2026-09-04 23:51:27 -05:00
.gitignore Initial commit 2026-09-04 23:51:27 -05:00
CHANGELOG.md release: prepare v0.3.0 release 2026-10-07 10:14:14 -05:00
Gemfile spec: cover Props with dry-types 2026-09-25 12:36:07 -05:00
LICENSE Initial commit 2026-09-04 23:51:27 -05:00
phlex-hanami.gemspec release: prepare v0.3.0 release 2026-10-07 10:14:14 -05:00
README.md release: prepare v0.2.0 release 2026-09-10 15:42:52 -05:00

Phlex::Hanami

An object oriented view layer for Hanami. Write views, layouts and components as Phlex classes instead of templates.

Installation

Add the gem to your Gemfile:

gem "phlex-hanami", "~> 0.2.0"

Or, from gem.coop:

gem "phlex-hanami", "~> 0.2.0", source: "https://gem.coop/@aaron"

Note

@stephannv wrote this gem and @aaronmallen now maintains it. 0.1.0 on RubyGems is theirs and shares no code with what is here. Releases from 0.2.0 on come from this repository.

Quick start

Give the app a base view:

# app/view.rb
module MyApp
  class View < Phlex::Hanami::View
  end
end

Expose what the view needs from the action:

# app/actions/posts/index.rb
module MyApp
  module Actions
    module Posts
      class Index < MyApp::Action
        include Deps["posts_repo"]

        def handle(_request, response)
          response[:posts] = posts_repo.all
        end
      end
    end
  end
end

Write the view under the Views namespace, with the matching name:

# app/views/posts/index.rb
module MyApp
  module Views
    module Posts
      class Index < MyApp::View
        def initialize(posts:) = @posts = posts

        def view_template
          h1 { "Posts" }
          ul do
            @posts.each { |post| li { a(href: path(:post, id: post.id)) { post.title } } }
          end
        end
      end
    end
  end
end

That is the whole setup. Hanami pairs the action with the view whose name matches and renders it. You write no response.render call and change no configuration.

Name a layout Views::Layout and every view in the slice renders inside it:

# app/views/layout.rb
module MyApp
  module Views
    class Layout < Phlex::Hanami::Layout
      def view_template
        doctype
        html do
          head do
            title { content_for(:page_title) || "MyApp" }
            link(rel: "stylesheet", href: asset_url("app.css"))
          end
          body { yield }
        end
      end
    end
  end
end

Documentation

  • Views: base classes, how a view is paired with an action, and what it receives
  • Layouts: the slice convention, per view overrides and opting out
  • Components: the component base class, kits, and sharing components between slices
  • View context: routes, assets, content_for, flash, session and request
  • Helpers: Hanami's helper library, escaping and translations
  • Responses: content types, JSON actions, HEAD requests and opting out of auto render
  • Mailers: Phlex views for hanami-mailer, and the plain text part
  • Testing: helpers for view and component specs, under RSpec or Minitest
  • How it works: what the gem installs, and why each piece is there

Roadmap

  • hanami generate producing Phlex views instead of ERB templates

Development

This project uses mise for tools and tasks. Run mise tasks to see them all.

mise run setup       # Install tools and dependencies
mise run test        # Run the test suite, with the RBS signatures checked as it goes
mise run format      # Format every file
mise run lint        # Lint every file

CONTRIBUTING covers the rest, including how the signatures are generated.

License

This project is licensed under the MIT License