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.

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.