Jskalc

Jskalc

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 :heart_eyes:

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? :sweat_smile:)
  • 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? :thinking:

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 :wink:

Showing Posts 1 to 10

Jskalc

Jskalc OP

Version 0.2.0 has been released. Changelog entry:

0.2.0 - 2024-05-17

QoL release

Added

  • @ added to Vite & typescript paths. Points to assets, so it’s possible to import files from @/img/someimage.svg. To migrate, see assets/copy/tsconfig.json and assets/copy/vite.config.js
  • Added Vite types to tsconfig.json to support special imports, eg. svg. To migrate, add "types": ["vite/client"].
  • Added possibility to colocate Vue files in lib directory. To migrate, copy assets/copy/vue/index.js to your project.

Changed

  • Adjusted files hierarchy to match module names
  • Publishing with expublish
Jskalc

Jskalc OP

Version 0.3.1 has been released. Lazy loading components is now supported :exploding_head::tada: Your app.js doesn’t have to grow as your application grow! SSR renders these lazy components + preload hints, so it has a minimal impact on performance :wink:

How to do it? Simply import files lazily using Vite

export default {
  ...import.meta.glob('./**/*.vue', { eager: true }),
  ...import.meta.glob('../../lib/**/*.vue', { eager: false })
}

in this example, Vue files colocated with elixir files will be loaded on-demand, while files in assets/vue will be always part of the app.js bundle. You can decide which files should be loaded on demand.

Another approach:

import component1 from './Component1.vue'
import component2 from './Component2.vue'
export default {
  Component1: component1,
  Component2: component2,
  Component3Lazy: () => import('./Component3.vue')
}

Changelog entries:

0.3.1 - 2024-05-17

Changed

  • Simplified assets/vue/index.js file - mapping filenames to keys is done by the library. Previous version should still work.

0.3.0 - 2024-05-17

CHANGED

  • removed esbuild from live_vue, package.json points directly to assets/js/live_vue
  • added support to lazy loading components. See more in README. To migrate, ensure all steps from installation are up-to-date.
Jskalc

Jskalc OP

Version 0.3.2 has been released. It included a fix to tailwind classes not being hot-updated correctly on phoenix change.

This allows to have an amazing DX: Hot code reload across the whole stack :heart_eyes:

Changelog:

0.3.2 - 2024-05-19

Fixed

  • Hot reload of CSS when updating Elixir files
arcanemachine

arcanemachine

You should rename the project to… anything that couldn’t be confused for LiveView.

Jskalc

Jskalc OP

I was already thinking about this.

  1. The name was a natural consequence of following LiveSvelte.
  2. In written english it’s perfect. Short, easy to comprehend and understand.
  3. In spoken english - yes, it’s not perfect. I realised it too late, after publishing the initial release and generating a bit of a buzz around it.

My idea to solve it - in speech it can be referred to as LiveVuejs. I added a section to the official FAQ. I’d prefer to avoid renaming package and all references to it.

Hopefully it won’t be a dealbreaker? :sweat_smile:

arcanemachine

arcanemachine

I totally get the part where it matches the LiveSvelte thing, it definitely follows logically. It’s just that it will, I 100% guarantee you, will lead to avoidable confusion.

When you have the choice of naming it literally anything else, why not make that choice, instead of stepping on the toes of what is possibly the Elixir community’s flagship project?

KP123

KP123

VueLive then you can market it as a Phoenix extension to Vue instead of a Vue extension to Phoenix

sodapopcan

sodapopcan

There is already a LiveViewJS which itself is confusing with the LiveView.JS module.

Jskalc

Jskalc OP

VueLive has almost the same problem, doesn’t it? I can imagine when speaking someone might reply “you meant LiveView?” and then you’d need to explain “yeah but I mean frontend framework Vue”

And also, it’s a Vue extension to Phoenix, not the other way around. Hard to market it that way.

Jskalc

Jskalc OP

Argh. Didn’t know that one. Do you think it’s well-known so renaming package from live_vue to live_vuejs will still cause confusion?

Where Next? Top

Trending in Announcing Top

wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
fuelen
Hi all! I want to present a small library which provides a mix task for generating an Entity-Relationship Diagram for Ecto schemas. You...
New
anuaralfetahe
Hello Published a new library - ProcessHub! ProcessHub is a library designed to manage process distribution within the Elixir cluster. ...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
sergio
It’s not that it’s vocabulary is too advanced. It’s something worse. I get lost trying to follow even a paragraph written by Claude. It’...
New
sorenone
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New
akoutmos
@hugobarauna, Dr. Dimitrios Koutmos (my brother) and I (Alex Koutmos) have been hard at work on writing a book on how you can use Elixir ...
New
pferriby
Introductory paragraph I’ll be looking for a keen junior or someone that has a couple of years experience in the real world (so you’ve be...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews