How to use slots in <.live_component>?

I recently started exploring LiveView.
I can’t figure out how to pass custom named slots into <.live_component>.

Here is my code:

<.live_component
    id="modal"
    module={Modal}
  >
    <:header>
      <p>Welcome from header</p>
    </:header>

    <:content>
      <p>Welcome</p>
    </:content>
  </.live_component>

Modal component:

def render(assigns) do
  assigns =
    assigns
    |> assign_new(:header, fn -> [] end)
    |> assign_new(:content, fn -> [] end)

  ~H"""
    <div class="bg-red-500" id={@id}>
      <div class="border-b-2 border-gray-300 bg-blue-500">
        <%= render_block(@header) %>
      </div>
      <%= render_block(@content) %>
    </div>
  """
end

Error I got:

key :header not found in: %{__changed__: %{flash: true, id: true, inner_block: true}, flash: %{}, id: "modal-1", inner_block: #Function<8.90377761/2 in LiveviewPlaygroundWeb.TestLive.render/1>, myself: %Phoenix.LiveComponent.CID{cid: 1}, socket: #Phoenix.LiveView.Socket<id: "phx-F0LTuDdKH6Jl5wOC", endpoint: LiveviewPlaygroundWeb.Endpoint, view: LiveviewPlaygroundWeb.TestLive, parent_pid: nil, root_pid: nil, router: LiveviewPlaygroundWeb.Router, assigns: #Phoenix.LiveView.Socket.AssignsNotInSocket<>, transport_pid: nil, ...>}

Thanks!

Take a look at Phoenix.Component.render_slot/2 documentation.

It doesn’t work.
I tried to copy example from docs and run it on my project, and again I have same error with different slot name. In this case :col.

Here it is:

key :col not found in: %{__changed__: %{flash: true, id: true, inner_block: true}, flash: %{}, id: "modal-1", inner_block: #Function<8.67617919/2 in LiveviewPlaygroundWeb.TestLive.render/1>, myself: %Phoenix.LiveComponent.CID{cid: 1}, socket: #Phoenix.LiveView.Socket<id: "phx-F0LYVWLVrrkJkA5k", endpoint: LiveviewPlaygroundWeb.Endpoint, view: LiveviewPlaygroundWeb.TestLive, parent_pid: nil, root_pid: nil, router: LiveviewPlaygroundWeb.Router, assigns: #Phoenix.LiveView.Socket.AssignsNotInSocket<>, transport_pid: nil, ...>}

I’m definitely doing something wrong.

All examples in documentation should work. If not then we would submit a bug report, but before that would happen just to be sure could you please share a minimal script to reproduce issue (ideally with example from documentation)?

You can use a minimal example of LiveView from here:

[edit] False: LiveComponents don’t have slots, only components.

You’d need to so something like:

<.live_component
    id="modal"
    module={Modal}
  >
  <.page_component>
    <:header>
      <p>Welcome from header</p>
    </:header>

    <:content>
      <p>Welcome</p>
    </:content>
  </.page_component>
  </.live_component>

2 Likes

Are you sure?

Slots

LiveComponent can also receive slots, in the same way as a Phoenix.Component:

Source: Slot section in Phoenix.LiveComponent

I just tried it in example script:


defmodule MyTable do
  use Phoenix.LiveComponent

  def render(assigns) do
    ~H"""
    <table>
      <tr>
        <%= for col <- @col do %>
          <th><%= col.label %></th>
        <% end %>
      </tr>
      <%= for row <- @rows do %>
        <tr>
          <%= for col <- @col do %>
            <td><%= render_slot(col, row) %></td>
          <% end %>
        </tr>
      <% end %>
    </table>
    """
  end
end

defmodule SamplePhoenix.SampleLive do
  use Phoenix.LiveView

  # …

  def mount(_params, _session, socket) do
    users = [
      %{address: "john.doe@example.com", name: "John Doe"},
      %{address: "sample@example.com", name: "Sample"}
    ]

    {:ok, assign(socket, :users, users)}
  end

  # …

  def render(assigns) do
    ~H"""
    <.live_component id="my-table" module={MyTable} rows={@users}>
      <:col :let={user} label="Name">
        <%= user.name %>
      </:col>

      <:col :let={user} label="Address">
        <%= user.address %>
      </:col>
    </.live_component>
    """
  end
end
3 Likes

You are right! My bad, I just tried it as well.