Vue template syntax compiled to native %Phoenix.LiveView.Rendered{} structs via Rust NIFs.
defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
use PhoenixVapor
def mount(_params, _session, socket), do: {:ok, assign(socket, count: 0)}
def render(assigns) do
~VUE"""
<div>
<p>{{ count }}</p>
<button @click="inc">+</button>
</div>
"""
end
def handle_event("inc", _, socket), do: {:noreply, update(socket, :count, &(&1 + 1))}
endSame WebSocket, same diff protocol, same LiveView client. No wrapper divs, no phx-update="ignore".
| Tier | What | How |
|---|---|---|
~VUE sigil |
Vue templates in any LiveView | ~VUE"""<p>{{ count }}</p>""" |
.vue SFC |
Complete LiveView from a .vue file |
use PhoenixVapor.Reactive, file: "Counter.vue" |
| Vapor DOM | Bypass morphdom — direct DOM writes | patchLiveSocket(liveSocket) |
def deps do
[
{:phoenix_vapor, "~> 0.1.0"},
{:quickbeam, "~> 0.3.0", optional: true} # for complex JS expressions
]
end{{ expr }} · :attr="expr" · @click="handler" · v-if / v-else-if / v-else · v-for · v-show · v-model · v-html · ternaries · arithmetic · .length · .toUpperCase() · dot access · components
Simple expressions evaluate in pure Elixir via OXC AST. Complex expressions (arrow functions, .filter(), .map()) fall back to QuickBEAM.
<script setup>
import { ref, computed } from "vue"
const count = ref(0)
const doubled = computed(() => count * 2)
function increment() { count++ }
</script>
<template>
<p>{{ count }} × 2 = {{ doubled }}</p>
<button @click="increment">+</button>
</template>defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
use PhoenixVapor.Reactive, file: "Counter.vue"
endref() → assigns, computed() → derived state, functions → event handlers. Three lines of Elixir.
Opt-in morphdom bypass. The client parses statics once, builds a registry mapping each dynamic slot to its DOM node, then applies diffs as direct property writes.
import { patchLiveSocket } from "phoenix_vapor"
patchLiveSocket(liveSocket)See ARCHITECTURE.md for protocol-level details.
- ARCHITECTURE.md — how it works at the protocol level, with wire format examples
- examples/demo/ — runnable Phoenix app with all features
MIT