Jskalc
LiveVue - v1.0 released with igniter installer, forms support and more!
LiveVue v1.0 released
After four release candidates and a lot of community feedback, LiveVue 1.0 is stable ![]()
I’ve built a dedicated website with interactive examples: livevue.skalecki.dev
And wrote the full story behind the library: The Story of LiveVue
What Changed
New Composables
useLiveForm — Server-side validation with Ecto changesets, nested objects, dynamic arrays. Fully typed:
<script setup lang="ts">
import { useLiveForm, type Form } from 'live_vue'
type UserForm = {
name: string
profile: { bio: string; skills: string[] }
}
const props = defineProps<{ form: Form<UserForm> }>()
const form = useLiveForm(() => props.form, {
changeEvent: 'validate',
submitEvent: 'submit'
})
// Type-safe field access — typos caught at compile time
const nameField = form.field('name')
const bioField = form.field('profile.bio')
const skillsArray = form.fieldArray('profile.skills')
</script>
<template>
<input v-bind="nameField.inputAttrs.value" />
<span v-if="nameField.errorMessage.value">{{ nameField.errorMessage.value }}</span>
<div v-for="(skill, i) in skillsArray.fields.value" :key="i">
<input v-bind="skill.inputAttrs.value" />
<button @click="skillsArray.remove(i)">Remove</button>
</div>
<button @click="skillsArray.add('')">Add Skill</button>
<button @click="form.submit()" :disabled="!form.isValid.value">Submit</button>
</template>
LiveView side stays exactly as you’d expect:
def handle_event("validate", %{"user" => params}, socket) do
changeset = User.changeset(%User{}, params) |> Map.put(:action, :validate)
{:noreply, assign(socket, form: to_form(changeset, as: :user))}
end
useLiveUpload — File uploads with progress tracking and drag-and-drop:
<script setup>
import { useLiveUpload } from 'live_vue'
const { entries, showFilePicker, addFiles, progress } = useLiveUpload(
() => props.upload,
{ submitEvent: 'save' }
)
</script>
<template>
<div @drop.prevent="e => addFiles(e.dataTransfer.files)" @dragover.prevent>
Drop files or <button @click="showFilePicker">browse</button>
<div v-for="entry in entries" :key="entry.ref">
{{ entry.client_name }} — {{ entry.progress }}%
</div>
</div>
</template>
useLiveConnection — Reactive WebSocket status for offline indicators:
<script setup>
import { useLiveConnection } from 'live_vue'
const { isConnected, connectionState } = useLiveConnection()
</script>
<template>
<div :class="isConnected ? 'text-green-500' : 'text-red-500'">
{{ connectionState }}
</div>
</template>
useEventReply — Bi-directional events with server responses:
<script setup>
import { useEventReply } from 'live_vue'
const { data, isLoading, execute } = useEventReply('fetch_user')
</script>
<template>
<button @click="execute({ id: 123 })" :disabled="isLoading">
{{ isLoading ? 'Loading...' : 'Fetch User' }}
</button>
<div v-if="data">{{ data.name }}</div>
</template>
Performance: JSON Patch Diffs
Props now use RFC 6902 JSON Patch. Only differences are sent:
[{"op": "replace", "path": "/users/1/name", "value": "Robert"}]
Inserting at the beginning of a 100-item list? One operation, not 100 — thanks to ID-based matching.
Phoenix Streams
Streams work transparently as reactive arrays:
<.vue messages={@streams.messages} v-component="Chat" v-socket={@socket} />
<script setup>
// messages is a reactive array — streams handled automatically
const props = defineProps<{ messages: Message[] }>()
</script>
Developer Experience
- One-command install:
mix igniter.install live_vue - No JS build step — TypeScript source exported directly, Vite handles it
- VS Code extension for
~VUEsigil syntax highlighting
Breaking Changes
shared_propsremoved — pass props explicitlynillify_not_loaded→nilify_not_loaded(typo fix)
Links
- Interactive examples: livevue.skalecki.dev
- Blog post: The Story of LiveVue
- Hex: hex.pm/packages/live_vue
- Docs: hexdocs.pm/live_vue
- GitHub: github.com/Valian/live_vue
- ElixirConf EU talk: Why mixing LiveView and a frontend framework is a great idea
Discuss and upvote also on Hacker News.
Happy New Year!
Trending in News & Updates
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #performance










First 5 of 5 Posts
mikehostetler
Great work! I’ve been mostly a React guy, but I haven’t been able to find a good flow when using React with LiveView, so this may convert me!
Jskalc
I would like to eventually port that work to LiveReact / LiveSvelte, since most of it should be easily transferrable. But for now I have to focus on other priorities
sezaru
Does
useLiveFormalso works with Ash.Changeset forms?I’m considering using LiveVue in a new project but that project will use Ash instead of Ecto directly.
Normally, when creating a form in LV for Ash, you will use AshPhoenix.Form — ash_phoenix v2.3.23 which will wrap around Phoenix forms, not sure if it can be used with LiveVue too.
sezaru
Another question, do you have some sample project examples of using LiveVue for the full project? In your post you mention postline.ai, but AFAIK, the code of that site is not open, so I can’t use as reference.
Jskalc
Hi @sezaru, sorry for a late response. Ash is not yet officialy supported, but I have a working version in my own project - just, not yet migrated to live vue. Since it’s important to you, I can prioritize it!
About examples - the best one currently is a github repo of https://livevue.skalecki.dev/, you can find it here.
I’m thinking about creating another example showing exactly how a simple intertia-style SaaS could be created with live_vue (exactly what I’m doing in Postline) - one component per page. Maybe it could be part of live vue website as well.
Thanks for being interested in live vue!