The missing Vue bridge for LiveView
When LiveView hooks get messy, reach for Vue. Full reactivity. No API layer. One install command.
mix igniter.install live_vue
Everything LiveView does,
Vue can too.
Streams, forms with Ecto validation, file uploads, server events — all work through Vue-native composables. You're not giving up LiveView features. You're extending them.
End-to-End Reactivity
LiveView assigns become Vue props automatically. When server state changes, Vue re-renders. When Vue emits events, LiveView handles them. The loop is complete.
Vue-native composables for LiveView features
useLiveVue()
pushEvent, handleEvent & more
useLiveForm()
Ecto changeset validation
useLiveUpload()
LiveView file uploads
useLiveEvent()
Server → client events
Plus phx-click, phx-change, and all Phoenix bindings work inside Vue templates.
$live
is available in templates for quick access.
Server-Side Rendered
Vue components render on first paint. No loading flash. Full SEO.
Efficient Updates
JSON patches over WebSocket. Only changed props are sent.
Vite-Powered DX
Instant HMR, TypeScript out of the box. The legendary Vite experience, for free.
One-Line Install
Igniter handles the setup. TypeScript, Vite, SSR — all configured.
Streams Support
Phoenix Streams work transparently. Efficient patches, no special handling.
Vue Ecosystem
Use any Vue library. Chart.js, TipTap, Headless UI — they all work.
AI-Ready
AGENTS.md auto-generated with LiveVue usage rules. Your AI assistant knows how to use it.
Lazy Loading
Load Vue components on demand. Keep your initial bundle small.
Server state meets
client interactivity
PubSub in action
Tabs or Spaces?
Props in. Events out.
Server owns the state.
<script setup lang="ts">
import { ref } from "vue"
// Props from LiveView
const props = defineProps<{ count: number }>()
// Local Vue state
const diff = ref(1)
</script>
<template>
<p>Count: {{ props.count }}</p>
<input v-model.number="diff" type="range" />
<!-- phx-click is also supported -->
<button @click="$live.pushEvent('inc', { diff })">
+{{ diff }}
</button>
</template>
defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
def render(assigns) do
~H"""
<.vue
count={@count}
v-component="Counter"
v-socket={@socket}
/>
"""
end
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
def handle_event("inc", %{"diff" => diff}, socket) do
{:noreply, update(socket, :count, &(&1 + diff))}
end
end
LiveView is great.
But sometimes not enough.
For most apps, LiveView handles everything. But when you need rich client-side interactions or want to tap into the Vue ecosystem, LiveVue bridges the gap.
Rich Interactions
Drag-drop, animations, complex local state that doesn't need the server.
Vue Ecosystem
Chart.js, TipTap, Headless UI — any Vue package works out of the box.
Team Familiarity
Your frontend team already knows Vue. Let them use what they know.
Gradual Adoption
Start with one component. Expand as needed. No big rewrites.
Ready to bridge the gap?
One command. Full TypeScript. SSR included. Start building.
mix igniter.install live_vue
Built by Jakub Skalecki
Freelance Elixir developer with 12+ years of fullstack experience. Startup founder who knows when to push back on bad ideas. Python, JavaScript, Elixir, Applied AI.
Read the story: Road to LiveVue 1.0Available for freelance Elixir projects