LiveVue 1.0
Examples Slots

Slots

Pass HEEx content into Vue components using slots. Combine the power of server-rendered templates with Vue's component composition.

What this example shows

1
Default Slot
Main content between tags
2
Named Slots
Header, footer, icon slots
3
Fallback Content
Default content when slot empty
slots_preview.ex
defmodule MyAppWeb.SlotsLive do
  use MyAppWeb, :live_view

  def render(assigns) do
    ~H"""
    <div class="space-y-6">
      <%!-- Example 1: Default slot only --%>
      <.vue v-component="Slots" v-socket={@socket}>
        <p class="text-landing-text">
          This is content in the <strong class="text-vue">default slot</strong>.
          No header or footer, just the main content area.
        </p>
      </.vue>

      <%!-- Example 2: Header + default --%>
      <.vue v-component="Slots" variant="primary" v-socket={@socket}>
        <:icon>
          <.icon name="hero-information-circle" class="w-5 h-5" />
        </:icon>
        <:header>
          <h3 class="font-medium text-lg text-landing-text">Card with Header & Icon</h3>
        </:header>
        <p class="text-landing-muted">
          This card demonstrates the header slot with an icon.
          The icon slot is positioned before the header text.
        </p>
      </.vue>

      <%!-- Example 3: All slots --%>
      <.vue v-component="Slots" variant="success" v-socket={@socket}>
        <:icon>
          <.icon name="hero-check-circle" class="w-5 h-5" />
        </:icon>
        <:header>
          <div>
            <h3 class="font-medium text-lg text-landing-text">Full Card Example</h3>
            <p class="text-sm text-landing-muted mt-1">With header, content, and footer slots</p>
          </div>
        </:header>
        <:footer>
          <div class="flex justify-between items-center">
            <span class="text-sm text-landing-muted">Updated 2 minutes ago</span>
            <button class="text-vue hover:underline text-sm font-medium">
              Learn more →
            </button>
          </div>
        </:footer>
        <p class="text-landing-text mb-3">
          This demonstrates all available slots working together.
          Each slot can contain any HEEx content, including components.
        </p>
        <ul class="list-disc list-inside text-landing-muted space-y-1 text-sm">
          <li>Icon slot appears in the header</li>
          <li>Header slot contains a title and subtitle</li>
          <li>Default slot holds the main content</li>
          <li>Footer slot provides actions</li>
        </ul>
      </.vue>

      <%!-- Example 4: Using title prop with fallback --%>
      <.vue v-component="Slots" title="Using Prop Fallback" v-socket={@socket}>
        <p class="text-landing-muted">
          When no header slot is provided, the
          <code class="text-phoenix bg-phoenix/10 px-1.5 py-0.5 rounded text-sm">title</code>
          prop
          is used as fallback content. This is a common pattern for flexible components.
        </p>
      </.vue>
    </div>
    """
  end

  def mount(_params, _session, socket) do
    {:ok, socket}
  end
end

How it works

1 Vue components define slot insertion points

In the Vue component, use <slot /> for the default slot and <slot name="header" /> for named slots. These act as placeholders for content passed from HEEx.

<slot />  <!-- default slot -->
<slot name="header" />  <!-- named slot -->

2 HEEx uses :slot_name syntax to pass content

In LiveView templates, content placed directly between the <.vue> tags becomes the default slot. Named slots use the :slot_name syntax.

<.vue v-component="Card" v-socket={@socket}>
  <:header>Title</:header>
  Default slot content
</.vue>

3 Slots can have fallback content

When a slot isn't provided, Vue renders the fallback content inside the <slot> tags. This is useful for optional sections or default states.

<slot name="footer">
  <p>Default footer content</p>
</slot>

4 Check slot presence in Vue

Use useSlots() to conditionally render sections based on whether a slot was provided. This enables flexible layouts that adapt to the content.

const slots = useSlots()
const hasFooter = computed(() => !!slots.footer)
Next up: Simple Form with useLiveForm()
View example →