Skip to main content

Overview

Phlex components are Ruby classes that inherit from Phlex::HTML, Phlex::SVG, or Phlex::CSV. They use the view_template method to define their output and support a rich lifecycle with hooks.

Creating Components

Every Phlex component needs a view_template method:
class HelloWorld < Phlex::HTML
  def view_template
    h1 { "Hello, World!" }
  end
end

Rendering Components

Rendering to a String

Use the call class method to render a component:
HelloWorld.call
# => "<h1>Hello, World!</h1>"
You can also instantiate and call:
component = HelloWorld.new
component.call
# => "<h1>Hello, World!</h1>"

Rendering Other Components

Use the render method to render other components:
class Layout < Phlex::HTML
  def view_template(&block)
    html do
      head { title { "My Site" } }
      body(&block)
    end
  end
end

class Page < Phlex::HTML
  def view_template
    render Layout.new do
      h1 { "Welcome" }
    end
  end
end
The render method accepts:
  • Phlex components (instances)
  • Component classes (will be instantiated)
  • Enumerables (renders each item)
  • Procs and Methods
  • Strings (outputs as plain text)

Component Initialization

Pass data to components through the initializer:
class UserCard < Phlex::HTML
  def initialize(name:, email:)
    @name = name
    @email = email
  end

  def view_template
    div class: "user-card" do
      h2 { @name }
      p { @email }
    end
  end
end

UserCard.new(name: "Alice", email: "[email protected]").call

Rendering Lifecycle

Phlex provides lifecycle hooks that execute in this order:
class MyComponent < Phlex::HTML
  def view_template
    h1 { "Main Content" }
  end

  private

  # Called before view_template
  def before_template
    # Setup logic here
  end

  # Wraps around view_template
  def around_template
    div class: "wrapper" do
      yield # Calls view_template
    end
  end

  # Called after view_template
  def after_template
    # Cleanup logic here
  end
end
Called before rendering the template. Useful for setup logic:
def before_template
  @start_time = Time.now
end

Content Blocks

Pass blocks to components for flexible layouts:
class Card < Phlex::HTML
  def view_template(&block)
    div class: "card" do
      if block_given?
        yield_content(&block)
      else
        p { "No content provided" }
      end
    end
  end
end

# Usage
render Card.new do
  h3 { "Title" }
  p { "Body text" }
end
Blocks passed to components are automatically stored and yielded during rendering. You don’t need to explicitly capture them in initialize.

Checking Render State

Use rendering? to check if a component is currently rendering:
def view_template
  if rendering?
    h1 { "Currently rendering" }
  end
end

Conditional Rendering

Override render? to conditionally skip rendering:
class AdminPanel < Phlex::HTML
  def initialize(user:)
    @user = user
  end

  def view_template
    h1 { "Admin Panel" }
    # ... admin content ...
  end

  private

  def render?
    @user.admin?
  end
end
If render? returns false, the component won’t render at all, including lifecycle hooks.

Context

Pass context between components:
# Pass context when rendering
component.call(context: { current_user: user })

# Access context in components
class MyComponent < Phlex::HTML
  def view_template
    user = context[:current_user]
    h1 { "Hello, #{user.name}" }
  end
end

Double Render Protection

Phlex prevents rendering the same component instance twice:
component = MyComponent.new
component.call # Works
component.call # Raises Phlex::DoubleRenderError
Create new instances if you need to render the same component multiple times.

Converting to Proc

Components can be converted to procs for use with enumerables:
class UserRow < Phlex::HTML
  def initialize(user)
    @user = user
  end

  def view_template
    tr do
      td { @user.name }
      td { @user.email }
    end
  end
end

# Use with render
render users.map { |u| UserRow.new(u) }

# Or more concisely
users.each(&UserRow.method(:new).to_proc)

Build docs developers (and LLMs) love