Hi! Today, after a couple weeks of development I’ve released v0.1 of LiveVue.
It’s a seamless integration of Vue and Phoenix LiveView, introducing E2E reactivity of server and client-side state.
Started as a fork of LiveSvelte, evolved to use Vite and a slightly different syntax.
Why you might want to use it?
- Your client-side state grows and it’s hard to deal with it
- You misses declarative rendering on the client side
- You’d like to use a vast ecosystem of Vue libraries
- You’d like to introduce animations
- You like Vue
Why I created it, if LiveSvelte already exists?
- I love Vue and was missing it’s DX and ecosystem (VueUse is amazing)
- More options are always better (right?
)
- Vite gives you best-in-class stateful-hot-reload, and paired with stateful-hot reload on the server you have a stateful hot reload across the whole stack (which is HUGE!)
Features:
- E2E reactivity
- Support for
phx-*
attributes inside Vue components - Uses Vite for an amazing DX
- Server Side Rendering (optional)
- Vue/Phoenix slots interop
- Vue event handlers can be defined with
JS
module ~V
sigil for inline Vue component definition
Plans for the future:
- On-demand lazy-loading of components
- Optimised payload with LiveJson or similar
- Better tests
- Dedicated page with examples
- Guide of handling changesets & forms
- Pinia support?
An example
defmodule LiveVueExamplesWeb.LiveCounter do
use LiveVueExamplesWeb, :live_view
def render(assigns) do
~H"""
<.vue
count={@count}
v-component="Counter"
v-socket={@socket}
v-on:inc={JS.push("inc")}
/>
"""
end
def mount(_params, _session, socket) do
{:ok, assign(socket, :counter, 0)}
end
def handle_event("inc", %{"value" => diff}, socket) do
{:noreply, update(socket, :count, &(&1 + diff))}
end
end
<script setup lang="ts">
import {ref} from "vue"
const props = defineProps<{count: number}>()
const emit = defineEmits<{inc: [{value: number}]}>()
const diff = ref<string>("1")
</script>
<template>
Current count
<div class="text-2xl text-bold">{{ props.count }}</div>
<label class="block mt-8">Diff: </label>
<input v-model="diff" class="my-4" type="range" min="1" max="10" />
<button
@click="emit('inc', {value: parseInt(diff)})"
class="bg-black text-white rounded p-2"
>
Increase counter by {{ diff }}
</button>
</template>
You can read some additional details in my short twitter thread.
I’d greatly appreciate a star on the github repo, and giving me any feedback if everything is working