Components

Components are the building blocks of Hologram applications. They are reusable pieces of UI that can be composed together to create complex interfaces.

Basic Example

Here's a simple stateless component that displays a greeting:

defmodule Greeting do
  use Hologram.Component

  prop :name, :string
  prop :title, :string, default: "Mr."

  def template do
    ~HOLO"""
    <p>Hello, {@title} {@name}!</p>
    """
  end
end

Usage:

<Greeting name="Wick" title="Mr." />

Props

Props are the primary way to pass data to components.

They are defined using the prop/2 macro receiving name and type params:

prop :user_id, :integer

or prop/3 macro that can receive the third opts param:

prop :user_id, :integer, default: 123

Types

Props can be typed using the following types:

Options

Props can be configured using the following options:

Default Value

Set a default value for optional props using the default option:

prop :count, :integer, default: 0

Context Source

Props can be sourced from the Context using the from_context option. This is useful for accessing shared data across components:

prop :user, :map, from_context: :current_user

For more information about Context, see the Context page.

Stateful vs. Stateless Components

Components can be either stateful or stateless:

Component ID (cid)

To make a component stateful, provide a cid when using it:

<Assassin cid="baba_yaga" name="John Wick" kill_count={439} />

The cid is used to target actions and commands at specific components using the target field:

<button $click={action: :increment_kills, target: "baba_yaga"}>Add Another One</button>

Initialization

A stateful component is initialized using either init/3 or init/2, depending on whether it is first rendered on the server or client:

Server-side Initialization

The init/3 function is used when the component is first loaded on the server. It receives three parameters:

The function should return either:

Example:

def init(_props, component, server) do
  component = put_state(component, :kill_count, 439)
  server = put_session(server, :name, "John Wick")

  {component, server}
end

Client-side Initialization

The init/2 function is used when the component is first loaded on the client. It receives two parameters:

The function should return a Component struct with any initial state:

def init(_props, component) do
  put_state(component, kill_count: 439, name: "John Wick")
end

Slots

Slots allow components to accept and render child content. They are defined using the <slot /> tag in the component's template:

defmodule Card do
  use Hologram.Component

  def template do
    ~HOLO"""
    <div class="card">
      <slot />
    </div>
    """
  end
end

Usage:

<Card>
  <h1>Card Title</h1>
  <p>Card content</p>
</Card>

Templates

Defining Templates

Hologram components can be templated using either a template function within the component module or a separate .holo file. These methods offer the same features and syntax - your choice depends on your team's coding style and project structure.

Template Function

def template do
  ~HOLO"""
  <div>Hello, {@name}!</div>
  """
end

Colocated .holo File

Create a file with the same name as your component's module file but with .holo extension (e.g., if your component is in my_component.ex, create my_component.holo). The .holo file must be located in the same directory as the module file.

Unlike the template function approach, colocated .holo files contain only the template markup without the ~HOLO sigil:

<div>Hello, {@name}!</div>

Template Syntax

See the Template Syntax documentation for more details.

Event Handling

Stateful components can handle user interactions and business logic through two mechanisms:

Example:

def action(:increment, params, component) do
  put_state(component, :count, component.state.count + params.by)
end

def command(:insert_user, params, server) do
  {:ok, user} = Repo.insert(%User{first_name: params.first_name})
  put_action(server, :user_inserted, user: user)
end

See the Actions and Commands documentation for more details.