Skip to main content

Phlex::SGML

The base class for HTML and SVG components in Phlex. Provides core rendering functionality, content output methods, lifecycle hooks, and caching capabilities.

Class Methods

.call(*args, **kwargs, &block)

Render the view to a String. Arguments are delegated to .new.
MyComponent.call(name: "Alice") # => "<div>Hello Alice</div>"
args
Array
Positional arguments passed to the component’s initializer
kwargs
Hash
Keyword arguments passed to the component’s initializer
block
Proc
Content block sent to view_template during rendering (not passed to initialize)

.new(*args, **kwargs, &block)

Create a new instance of the component.
The block is NOT delegated to #initialize. Instead, it will be sent to #view_template when rendering.
component = MyComponent.new(title: "Hello")
component.call # renders the component

Instance Methods

#view_template(&block)

Define the template for your component. Override this method in your component class.
class MyComponent < Phlex::HTML
  def view_template
    h1 { "Hello World" }
  end
end
block
Proc
Optional content block passed during rendering

#call(buffer = +"", context: {}, fragments: nil, &block)

Render the component and return the output string.
component = MyComponent.new
html = component.call
buffer
String
default:"+\"\""
Output buffer to append to
context
Hash
default:"{}"
Hash of context data available during rendering via #context
fragments
Array
Array of fragment names to selectively render
block
Proc
Content block to render
A component can only be rendered once. Attempting to render the same instance twice raises Phlex::DoubleRenderError.

#context

Access the context hash passed to #call.
def view_template
  h1 { context[:title] }
end

# Usage:
MyComponent.new.call(context: { title: "Welcome" })
Raises Phlex::ArgumentError if called before the component has started rendering.

#rendering?

Returns false before rendering and true once the component has started rendering. Does not reset to false after rendering.
def initialize
  rendering? # => false
end

def view_template
  rendering? # => true
end

#plain(content)

Output plain text content with automatic HTML escaping.
plain "Hello <World>" # => "Hello &lt;World&gt;"
plain 42               # => "42"
plain :symbol          # => "symbol"
content
String | Symbol | Integer | Float
Content to output as plain text
Supports String, Symbol, Integer, and Float types. Other types require custom format_object implementation.

#whitespace(&block)

Output a single space character. If a block is given, a space will be output before and after the block.
span { "Hello" }
whitespace
span { "World" }
# => "<span>Hello</span> <span>World</span>"

whitespace { span { "Middle" } }
# => " <span>Middle</span> "

#comment(&block)

Wrap the output in an HTML comment.
comment { plain "This is a comment" }
# => "<!-- This is a comment -->"
MDN Docs

#raw(content)

Output the given safe object as-is without escaping. Use #safe to mark a string as safe.
raw safe("<strong>Bold</strong>")
# => "<strong>Bold</strong>"
content
Phlex::SGML::SafeObject
Safe object to output without escaping
Raises Phlex::ArgumentError if passed an unsafe object. Always use #safe to mark strings as safe.

#safe(value)

Mark a string as safe for HTML output, preventing automatic escaping.
script { safe("console.log('Hello');") }
# => "<script>console.log('Hello');</script>"

a(onclick: safe("window.history.back()"))
# => '<a onclick="window.history.back()"></a>'
value
String
String to mark as safe
Alias: 🦺

#capture(*args, &block)

Capture the output of a block and return it as a string instead of appending to the buffer.
title = capture { h1 { "Hello" } }
# title => "<h1>Hello</h1>"

with_args = capture("a", "b") { |x, y| "#{x} #{y}" }
# with_args => "a b"
args
Array
Arguments to pass to the block
block
Proc
required
Block to capture output from

#fragment(name, &block)

Define a named fragment that can be selectively rendered.
fragment(:header) do
  h1 { "Title" }
end

fragment(:content) do
  p { "Body text" }
end

# Render only specific fragments:
call(fragments: [:header]) # renders only the header
name
Symbol | String
required
Name of the fragment

#flush

Flush the current state buffer to the output buffer.
flush # Forces immediate output of buffered content

#render(renderable = nil, &block)

Render another component, enumerable, string, or proc.
# Render a component instance
render MyComponent.new

# Render a component class
render MyComponent

# Render multiple components
render [Header.new, Body.new, Footer.new]

# Render a string
render "Hello"

# Render a proc
render -> { h1 { "Dynamic" } }
renderable
Phlex::SGML | Class | Enumerable | Proc | Method | String
Object to render

#cache(*cache_key, **options, &content)

Cache a block of content with an automatically generated cache key.
@products.each do |product|
  cache product do
    h1 { product.name }
    p { product.description }
  end
end
cache_key
Array
Custom cache key components (combined with automatic keys)
options
Hash
Options passed to the cache store
The full cache key includes: app version, class name, object_id (if reloading enabled), method name, line number, and your custom cache key.

#low_level_cache(cache_key, **options, &content)

Cache a block where you control the entire cache key.
low_level_cache([Commonmarker::VERSION, Digest::MD5.hexdigest(@content)]) do
  markdown(@content)
end
cache_key
Array | String
required
Complete cache key (you have full control)
options
Hash
Options passed to the cache store
Unlike #cache, this method does not take a splat. Pass an array if you need multiple cache key components.

#json_escape(string)

Escape a string for safe use in JSON contexts.
json_escape("Hello \"World\"")
# => "Hello \\\"World\\\""
string
String
required
String to escape for JSON

#to_proc

Convert the component to a Proc that can be used with collection methods.
@items.map(&MyComponent.new)

Lifecycle Hooks

Override these private methods to hook into the rendering lifecycle:

#around_template(&block)

Wrap the entire template rendering. Call yield to render the template.
private def around_template
  puts "Before template"
  yield
  puts "After template"
end

#before_template(&block)

Called before rendering the template.
private def before_template
  @start_time = Time.now
end

#after_template(&block)

Called after rendering the template.
private def after_template
  puts "Rendered in #{Time.now - @start_time}s"
end

#render?

Control whether the component should render. Return false to skip rendering.
private def render?
  current_user.admin?
end

Customization Hooks

#format_object(object)

Customize how objects are formatted for output. Override to support additional types.
private def format_object(object)
  case object
  when Float, Integer
    object.to_s
  when BigDecimal
    object.to_s('F')
  end
end

#cache_store

Provide a cache store implementation for #cache and #low_level_cache.
private def cache_store
  Rails.cache
end

#app_version_key

Override to use a different deployment key for cache invalidation.
private def app_version_key
  ENV['APP_VERSION']
end

#enable_cache_reloading?

Enable cache reloading during development. Returns false by default.
private def enable_cache_reloading?
  Rails.env.development?
end

Build docs developers (and LLMs) love