Frequently Asked Questions
View SourceGeneral Questions
Why LiveVue?
Phoenix LiveView makes it possible to create rich, interactive web apps without writing JS. However, when you need complex client-side functionality, you might end up writing lots of imperative, hard-to-maintain hooks.
LiveVue allows you to create hybrid apps where:
- Server maintains the session state
- Vue handles complex client-side interactions
- Both sides communicate seamlessly
Common use cases:
- Your hooks are starting to look like jQuery
- You have complex local state to manage
- You want to use the Vue ecosystem (transitions, graphs, etc.)
- You need advanced client-side features
- You simply like Vue 😉
What's with the Name?
Yes, "LiveVue" sounds exactly like "LiveView" - we noticed slightly too late to change! Some helpful Reddit users pointed it out 😉
We suggest referring to it as "LiveVuejs" in speech to avoid confusion.
Technical Details
How Does LiveVue Work?
The implementation is straightforward:
Rendering: Phoenix renders a
div
with:- Props as data attributes
- Slots as child elements
- Event handlers configured
- SSR content (when enabled)
Initialization: The LiveVue hook:
- Mounts on element creation
- Sets up event handlers
- Injects the hook for
useLiveVue
- Mounts the Vue component
Updates:
- Phoenix updates only changed data attributes
- Hook updates component props accordingly
Cleanup:
- Vue component unmounts on destroy
- Garbage collection handles cleanup
Note: Hooks fire only after app.js
loads, which may cause slight delays in initial render.
For a deeper dive into the architecture, see Architecture.
What Optimizations Does LiveVue Use?
LiveVue implements several performance optimizations:
Selective Updates:
- Only changed props/handlers/slots are sent to client
- Achieved through careful
__changed__
assign modifications
Efficient Props Handling:
data-props={"#{@props |> Jason.encode()}"}
String interpolation prevents sending
data-props=
on each updateStruct Encoding and Diffing:
- Uses
LiveVue.Encoder
protocol to convert structs to maps - Enables efficient JSON patch calculations (using Jsonpatch library)
- Reduces payload sizes by sending only changed fields
- Uses
Coming Soon:
- Sending only updated props
What is the LiveVue.Encoder Protocol?
The LiveVue.Encoder
protocol is a crucial part of LiveVue's architecture that safely converts Elixir structs to maps before JSON serialization. It serves several important purposes:
Why it's needed:
- Security: Prevents accidental exposure of sensitive struct fields
- Performance: Enables efficient JSON patch diffing by providing consistent data structures
- Explicit Control: Forces developers to be intentional about what data is sent to the client
How to use it:
defmodule User do
@derive LiveVue.Encoder
defstruct [:name, :email, :age]
end
For complete implementation details including field selection, custom implementations, and third-party structs, see Component Reference.
Without implementing this protocol, you'll get a Protocol.UndefinedError
when trying to pass custom structs as props. This is by design - it's a safety feature to prevent accidental data exposure.
The protocol is similar to Jason.Encoder
but converts structs to maps instead of JSON strings, which allows LiveVue to calculate minimal diffs and send only changed data over WebSocket connections.
Why is SSR Useful?
SSR (Server-Side Rendering) provides several benefits:
- Initial Render: Components appear immediately, before JS loads
- SEO: Search engines see complete content
- Performance: Reduces client-side computation
Important notes:
- SSR runs only during "dead" renders (no socket)
- Not needed during live navigation
- Can be disabled per-component with
v-ssr={false}
For complete SSR configuration, see Configuration.
Can I nest LiveVue components inside each other?
No, it is not possible to nest a <.vue>
component rendered by LiveView inside another <.vue>
component's slot.
Why?
This limitation exists because of how slots are handled. The content you place inside a component's slot in your .heex
template is rendered into raw HTML on the server before being sent to the client. When the parent Vue component is mounted on the client, it receives this HTML as a simple string.
Since the nested component's HTML is just inert markup at that point, Phoenix LiveView's hooks (including the VueHook
that powers LiveVue) cannot be attached to it, and the nested Vue component will never be initialized.
Workarounds:
Adjacent Components: The simplest approach is to restructure your UI to use adjacent components instead of nested ones.
# Instead of this: <.Card v-socket={@socket}> <.UserProfile user={@user} v-socket={@socket} /> </.Card> # Do this: <.Card v-socket={@socket} /> <.UserProfile user={@user} v-socket={@socket} />
Standard Vue Components: You can nest standard (non-LiveVue) Vue components inside a LiveVue component. These child components are defined entirely within the parent's Vue template and do not have a corresponding
<.vue>
tag in LiveView. They can receive props from their LiveVue parent and manage their own state as usual.<!-- Parent: MyLiveVueComponent.vue --> <script setup> import StandardChild from './StandardChild.vue'; defineProps(['someData']); </script> <template> <div> <h1>Data from LiveView: {{ someData }}</h1> <StandardChild :data-from-parent="someData" /> </div> </template>
Development
How Do I Use TypeScript?
LiveVue provides full TypeScript support:
- Use the example
tsconfig.json
- Check
example_project/assets/ts_config_example
for:- LiveVue entrypoint
- Tailwind setup
- Vite config
For app.js
, since it's harder to convert directly:
// Write your code in TypeScript
// app.ts
export const initApp = () => { /* ... */ }
// Import in app.js
import {initApp} from './app.ts'
initApp()
For complete TypeScript setup, see Configuration.
Where Should I Put Vue Files?
Vue files in LiveVue are similar to HEEX templates. You have two main options:
- Default Location:
assets/vue
directory - Colocated: Next to your LiveViews in
lib/my_app_web
Colocating provides better DX by:
- Keeping related code together
- Making relationships clearer
- Simplifying maintenance
No configuration needed - just place .vue
files in lib/my_app_web
and reference them by name or path.
For advanced component organization, see Configuration.
Comparison with Other Solutions
How Does LiveVue Compare to LiveSvelte?
Both serve similar purposes with similar implementations, but have key differences:
Technical Differences:
- Vue uses virtual DOM, Svelte doesn't
- Vue bundle is slightly larger due to runtime
- Performance is comparable
Reactivity Approach:
- Svelte: Compilation-based, concise but with some limitations
- Vue: Proxy-based, more verbose but more predictable
Future Developments:
- Vue is working on Vapor mode (no virtual DOM)
- Svelte 5 Runes will be similar to Vue
ref
Ecosystem:
- Vue has a larger ecosystem
- More third-party components available
- Larger community
Choose based on:
- Your team's familiarity
- Ecosystem requirements
- Syntax preferences
- Bundle size concerns
For a detailed comparison with other solutions, see Comparison.