Skip to content

Lesson 1 — Installation and the generator

Installing phlex-rails

1
2
bundle add phlex-rails
bundle add literal

Then run the install generator:

1
bundle exec rails generate phlex:install

What the generator does

Rather than just running it and moving on, let’s look at what it actually creates — understanding this makes the rest of the module much clearer.

The generator creates or modifies these files: config/initializers/phlex.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# frozen_string_literal: true

module Views
end

module Components
  extend Phlex::Kit
end

Rails.autoloaders.main.push_dir(
  "#{Rails.root}/app/views",
  namespace: Views
)

Rails.autoloaders.main.push_dir(
  "#{Rails.root}/app/components",
  namespace: Components
)

This is the heart of the Phlex Rails setup. It defines two modules — Views and Components — and tells Zeitwerk to load files from app/views under the Views:: namespace and app/components under the Components:: namespace. Notice that Components already has extend Phlex::Kit — the Kit is configured automatically. This replaces the manual setup we did in base.rb in Module 3.

app/components/base.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# frozen_string_literal: true

class Components::Base < Phlex::HTML
  include Phlex::Rails::Helpers::Routes

  if Rails.env.development?
    def before_template
      comment { "Before #{self.class.name}" }
      super
    end
  end
end

The base class for all components. Two things worth noting:

include Phlex::Rails::Helpers::Routes gives every component access to Rails route helpers (boards_path, new_board_path etc.) without any additional setup.

The before_template hook is gated behind Rails.env.development? — in development it injects an HTML comment before each component renders, so your browser dev tools show exactly which component produced which chunk of HTML:

1
2
3
4
5
<!-- Before Components::Button -->
<button type="button" class="...">Save</button>

<!-- Before Views::Boards::Index -->
<div class="...">

Zero impact in production. Extremely useful for debugging.

app/views/base.rb

1
2
3
class Views::Base < Components::Base
  def cache_store = Rails.cache
end

The base class for all views. It inherits from Components::Base rather than Phlex::HTML directly — which means every view automatically gets Routes helpers, the before_template debug comments, and anything else we add to Components::Base. No duplication needed.

cache_store = Rails.cache wires Phlex’s built-in fragment caching to the Rails cache store. We cover caching in Module 9 — for now just know it’s there.

The full inheritance chain:

Phlex::HTML
  └── Components::Base    (Routes, dev-mode comments)
        ├── Components::Button
        ├── Components::Card
        └── Views::Base   (cache_store)
              ├── Views::Boards::Index
              └── Views::Boards::Show

Extending the generated base classes

The generated Components::Base is minimal. We need to add Literal::Properties for typed props and our class_names helper. Importantly, preserve the generated before_template hook — don’t replace the file, extend it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# app/components/base.rb
# frozen_string_literal: true

class Components::Base < Phlex::HTML
  include Phlex::Rails::Helpers::Routes
  extend Literal::Properties

  if Rails.env.development?
    def before_template
      comment { "Before #{self.class.name}" }
      super
    end
  end

  private

  def class_names(*classes)
    classes.flatten.compact.reject(&:empty?).join(" ")
  end
end

Views::Base needs no changes — it inherits everything through Components::Base. The Kit is also already available in views through the inheritance chain since Views::Base descends from Components::Base which lives inside the Components module.

The autoloading convention

With Zeitwerk handling autoloading, you no longer need require_relative in your component files. The file naming convention does the work:

app/components/button.rb        → Components::Button
app/components/card.rb          → Components::Card
app/views/boards/index.rb       → Views::Boards::Index
app/views/layouts/app_layout.rb → Views::Layouts::AppLayout

Remove all require_relative "base" lines from your component files — they are no longer needed and will cause errors if left in.

Generators

Phlex provides generators for creating views and components:

1
2
bundle exec rails g phlex:view Boards::Index
bundle exec rails g phlex:component Button

These create the file in the correct location with the correct namespace. We will use them throughout the module.

Exercise

Run the generator and inspect the created files. Then update Components::Base and Views::Base as shown. Remove require_relative from all component files ported from Module 3. Verify by starting the Rails server — if autoloading is working you will see no NameError on boot.

1
bin/rails server