Documentation Index Fetch the complete documentation index at: https://mintlify.com/Valian/live_vue/llms.txt
Use this file to discover all available pages before exploring further.
Now that you have LiveVue installed, let’s create your first Vue component and integrate it with LiveView.
Creating your first component
Let’s build a simple counter component that demonstrates the reactivity between Vue and LiveView.
Create the Vue component
Create assets/vue/Counter.vue: < script setup lang = "ts" >
import { ref } from "vue"
// props are passed from LiveView
const props = defineProps <{ count : number }>()
// local state
const diff = ref ( 1 )
</ script >
< template >
Current count: {{ props . count }}
< label > Diff: </ label >
< input v-model . number = " diff " type = "range" min = "1" max = "10" />
< button phx-click = "inc" : phx-value-diff = " diff " >
Increase counter by {{ diff }}
</ button >
</ template >
Create the LiveView
Create a LiveView to handle the counter state at lib/my_app_web/live/counter_live.ex: 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" => value}, socket) do
{ :noreply , update (socket, :count , & ( &1 + value))}
end
end
Add the route
Add the route in your router.ex: live "/counter" , CounterLive
Test it out
Start your server and visit http://localhost:4000/counter to see your counter in action!
Adding smooth transitions
One of Vue’s strengths is its built-in transition system. Let’s enhance our counter with smooth animations and Tailwind styling.
Create animated component
Create assets/vue/AnimatedCounter.vue: < script setup lang = "ts" >
import { ref } from "vue"
const props = defineProps <{ count : number }>()
const diff = ref ( 1 )
</ script >
< template >
< div class = "space-y-4" >
< div class = "text-center" >
< Transition name = "count" mode = "out-in" >
< span
: key = " props . count "
class = "text-4xl font-bold text-blue-600"
>
{{ props . count }}
</ span >
</ Transition >
</ div >
< div class = "flex items-center gap-4" >
< label class = "text-sm font-medium" > Diff: </ label >
< input
v-model . number = " diff "
type = "range"
min = "1"
max = "10"
class = "flex-1"
/>
< span class = "text-sm text-gray-600" > {{ diff }} </ span >
</ div >
< button
phx-click = "inc"
: phx-value-diff = " diff "
class = "w-full px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
>
Increase by {{ diff }}
</ button >
</ div >
</ template >
< style scoped >
.count-enter-active ,
.count-leave-active {
transition : all 0.3 s ease ;
}
.count-enter-from {
opacity : 0 ;
transform : scale ( 0.8 ) translateY ( -10 px );
}
.count-leave-to {
opacity : 0 ;
transform : scale ( 1.2 ) translateY ( 10 px );
}
</ style >
Update your LiveView
Update your LiveView to use the animated version: def render (assigns) do
~H"""
< div class = "max-w-sm mx-auto mt-8 p-6 bg-white rounded-lg shadow-lg" >
< h1 class = "text-2xl font-bold mb-6 text-center" > Animated Counter </ h1 >
<.vue count={@count} v-component="AnimatedCounter" v-socket={@socket} />
</ div >
"""
end
Now your counter will smoothly animate when the value changes! This showcases how Vue’s transition system can add polish to your LiveView apps without any server-side complexity.
Key concepts
This example demonstrates several important LiveVue features:
Props flow LiveView sends the count value to Vue as a prop
Event handling Vue emits events with phx-click and phx-value-* attributes
State management LiveView maintains the source of truth (the counter value)
Local UI state Vue maintains the slider value locally without server involvement
Transitions Vue handles smooth animations purely on the client side
Efficient updates Only changed data is sent over WebSocket using JSON Patch
Working with custom structs
When you start passing more complex data structures as props, you’ll need to implement the LiveVue.Encoder protocol:
# For any custom structs you want to pass as props
defmodule User do
@derive LiveVue . Encoder
defstruct [ :name , :email , :age ]
end
# Use in your LiveView
def render (assigns) do
~H"""
<.vue user={@current_user} v-component="UserProfile" v-socket={@socket} />
"""
end
This protocol ensures that:
Only specified fields are sent to the client
Sensitive data is protected from accidental exposure
Props can be efficiently diffed for optimal performance
Development tips
Good to know:
Install the Vue DevTools browser extension for debugging
In development, LiveVue enables Hot Module Replacement for instant component updates
Structure your app with LiveView managing application state and Vue handling UI interactions
For complex UIs with lots of local state, prefer Vue components over LiveView hooks
Next steps
Now that you have your first component working, explore:
Live examples Interactive demos to see LiveVue in action
Basic usage Learn more patterns and the ~VUE sigil
Component reference Complete syntax documentation
FAQ Common questions and troubleshooting