LiveVue 1.0
Examples Counter

Counter

The classic counter example demonstrating the core LiveVue pattern: server state flows down as props, user interactions bubble up as events.

What this example shows

1
Server State
count lives in LiveView
2
Local State
diff slider is Vue-only
3
Phoenix Events
phx-click in Vue template
4
Vue Transitions
<Transition> on prop changes
Counter.vue
<script setup lang="ts">
import { ref } from "vue"

const props = defineProps<{ count: number }>()
const diff = ref(1)
</script>

<template>
  <div class="card bg-base-200 p-6">
    <div class="text-center mb-6">
      <div class="text-sm text-neutral mb-2">Count</div>
      <Transition name="count" mode="out-in">
        <div :key="props.count" class="text-5xl font-mono font-bold">
          {{ props.count }}
        </div>
      </Transition>
    </div>

    <div class="mb-6">
      <label class="flex justify-between text-sm text-neutral mb-2">
        <span>Step</span>
        <span class="font-mono text-primary">{{ diff }}</span>
      </label>
      <input
        v-model.number="diff"
        type="range"
        min="1"
        max="10"
        class="range range-primary range-sm"
      />
    </div>

    <div class="flex gap-2">
      <button phx-click="inc" :phx-value-diff="-diff" class="btn btn-outline flex-1">
        −{{ diff }}
      </button>
      <button phx-click="inc" :phx-value-diff="diff" class="btn btn-secondary flex-1">
        +{{ diff }}
      </button>
      <button phx-click="reset" class="btn btn-ghost text-neutral">
        Reset
      </button>
    </div>
  </div>
</template>

<style scoped>
.count-enter-active,
.count-leave-active {
  transition: all 0.15s ease;
}

.count-enter-from {
  opacity: 0;
  transform: translateY(-8px);
}

.count-leave-to {
  opacity: 0;
  transform: translateY(8px);
}
</style>

How it works

1 Props flow from LiveView to Vue

The count assign in LiveView becomes a prop in Vue. When the server state changes, Vue automatically re-renders with the new value.

<.vue count={@count} v-component="Counter" v-socket={@socket} />

2 Local state stays in Vue

The diff slider value is managed entirely in Vue using ref(). No server round-trip for UI-only state.

const diff = ref(1)  // Local Vue state

3 Phoenix bindings work in Vue templates

Standard Phoenix event bindings like phx-click work directly in Vue templates. Use Vue's :phx-value-* binding to pass dynamic values.

<button phx-click="inc" :phx-value-diff="diff">+{{ diff }}</button>

4 Vue transitions on server updates

Vue's <Transition> component animates when props change from the server. The :key binding triggers the transition on each value change.

<Transition name="count" mode="out-in">
<div :key="props.count">{{ props.count }}</div>
</Transition>
Next up: Event Handling
View example →