v

Migrating from petite-vue

petite-vue’s last release, 0.4.1, was published in January 2022. It still gets around ten thousand downloads a month, so plenty of pages are running it, and nothing about it has broken. But it is not being maintained, and if you are reading this you have probably already noticed.

This page maps every petite-vue directive to its sprae equivalent. It also says plainly what you lose, because one thing does not survive the move cleanly.

Quick reference

petite-vue sprae
v-scope="{ count: 0 }" :scope="{ count: 0 }"
v-scope (no value) :scope
{{ count }} :text="count" (see below)
v-text="msg" :text="msg"
v-html="raw" :html="raw"
v-if / v-else-if / v-else :if / :else :if / :else
v-for="item in items" :each="item in items"
v-for="(item, i) in items" :each="item, i in items"
v-show="open" :hidden="!open"
:class / v-bind:class :class
:style / v-bind:style :style
:disabled / v-bind:disabled :disabled
v-bind="obj" :="obj"
@click / v-on:click :onclick
@click.prevent.stop :onclick.prevent.stop
@keyup.enter :onkeydown.enter
v-model="q" :value="q" :change="v => q = v"
v-effect="..." :fx="..."
@vue:mounted :mount
@vue:unmounted return a cleanup from :mount
ref="input", $refs.input :ref="input"
$el this, or :ref
v-cloak CSS: [\:scope] { visibility: hidden }
reactive(obj) store(obj) from sprae/store
createApp(state).mount() sprae(el, state)
createApp().directive(name, fn) sprae.use({ dir: { name: fn } })

Text interpolation

This is the one real cost of the move. petite-vue supports mustaches; sprae has no interpolation, because it never parses text nodes — it only reads attributes.

<!-- petite-vue -->
<p>Hello, {{ name }}! You have {{ items.length }} items.</p>

<!-- sprae -->
<p><span :text="'Hello, ' + name + '!'"></span>
   You have <span :text="items.length"></span> items.</p>

In exchange, the text you write in the HTML is what a crawler and a reader with JavaScript off actually see, instead of raw mustaches flashing before hydration. There is no v-cloak problem to solve because there is nothing to hide. If most of your templating is mustache-heavy prose, budget for this; if it is mostly attributes and lists, you will barely notice.

Two-way binding is two directives

petite-vue’s v-model does both directions. sprae splits it:

<!-- petite-vue -->
<input v-model="query" />

<!-- sprae -->
<input :value="query" :change="v => query = v" />

It is one attribute longer, and it buys you the write-back path: :change hands you the value already coerced to the right type, so you can validate, clamp, format or debounce it without giving up the binding.

<input :value="search" :change.debounce-300="v => search = v" />

Components

petite-vue components are functions returning a scope object, optionally with $template. sprae takes the function directly:

<!-- petite-vue -->
<script>
  function Counter({ initialCount }) {
    return { count: initialCount, inc() { this.count++ } }
  }
</script>
<div v-scope="Counter({ initialCount: 1 })">
  <button @click="inc" v-text="count"></button>
</div>

<!-- sprae -->
<script>
  window.Counter = (scope) => ({ count: 1, inc() { count++ } })
</script>
<div :scope="Counter">
  <button :onclick="inc()" :text="count"></button>
</div>

There is no $template equivalent. Reuse markup with your server template’s own includes, with a <template> element and :html, or with a custom element — sprae sets a custom element’s props and stops at its boundary.

What changes for the better

What petite-vue still does fine

Being honest about this is the point: if none of the above is a problem for you, petite-vue is not broken. It is 6.4kb, it is Vue-compatible enough that Vue muscle memory transfers, and mustache interpolation is genuinely more pleasant for text-heavy templates. A page that works today will keep working.

The reasons to move are: you want a maintained dependency, you need CSP or extension support, you hit collection reactivity, or you are already writing around a bug nobody is going to fix.

Moving over

Sizes and speed against Alpine and petite-vue are on the comparison page, measured and reproducible. The whole directive set is on the reference, and there are working examples of the usual patterns.

<script src="https://unpkg.com/sprae" defer></script>