Why did I create vue-uform

posted 2 min read

In Vue, we already have the very convenient v-model syntax sugar. It handles two essential things for us:

  • When a JavaScript variable has a value, it fills the input field.
  • When the input changes, the new value is passed back to JavaScript.

That’s basically what v-model does under the hood.

So why redefine two-way binding?

The reason is that I’ve been exploring a new approach to form validation. I believe the next generation of Vue form validators should be:

  • Component-driven – the form behavior is powered by components, not configuration.
  • Style-agnostic – no built-in styling, leaving developers free to define layouts.
  • Flexible with UI libraries – should work seamlessly with input components from libraries like Naive UI, Element Plus, or even fully custom ones.

What existing validators do

When I looked at existing solutions, I found a common pattern: each form field usually comes with:

  • A label
  • An input
  • A help message
  • An error message (shown when validation fails)

Some libraries solve this by bundling their own input components and tightly integrating them with validation. While this works, it reduces flexibility.

I wanted something different: a validator that lets developers use their own UI components, while still handling value binding and validation logic cleanly.

The idea: u-field component

To achieve this, I introduced the idea of a u-field component.
You can put any input component into its slot.

The u-field exposes two key bindings to its slot:

  • value passes the form data down to the input.
  • update sends the new input value back up to the form.

This makes synchronization possible, and sets the stage for validation.

For example:

<template>
  <u-field v-slot="{ value, update }">
    <input :value="value" @input="($event) => update($event.target.value)" />
  </u-field>
</template>

Why not just v-model?

Looking at Vue’s official docs, this is exactly the kind of code v-model was meant to simplify.
But when binding with parent components (not just local state), we need another kind of syntax sugar.

So, inspired by vue-macros, I created a Vite plugin to introduce a new directive: f-model.

With f-model, the example above becomes:

<u-field v-slot="{ value, update }">
  <input f-model />
</u-field>

Beyond text inputs

Of course, text inputs are the simplest case.
For other components like radio buttons, checkboxes, or selects, special handling is still needed.

For third-party components, it’s straightforward:

  • With a regular input:
<input f-model />
  • With Naive UI’s n-input (which uses v-model:value):
<n-input f-model:value />
0 votes
0 votes

More Posts

I built vue-uform – a unstyled, component-driven form validation library for Vue 3

tu6ge - Aug 18

How I Started Writing Unit Tests for Vue Components - Part 2

ByteMinds - Nov 21

How I Built a Smart Elevator Control System in Vue 3 and TypeScript

Toni Naumoski - Jun 19

How I Started Writing Unit Tests for Vue Components

ByteMinds - Mar 29

How to apply various postprocessing effects like Bloom, Motion Blur, and Pixelation inside a Vue

Sunny - Jul 6
chevron_left