LiveVue
1.0
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
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)