1. The Problem with Most Form Libraries
When building forms in Vue, I’ve often found that existing solutions fall into one of two camps:
- Overly opinionated – They ship with a lot of styling and layout assumptions that don’t match your UI framework or design system.
- Too much boilerplate – Setting up form values, validation rules, and event handlers requires a ton of JS/TS code for even basic forms.
If you’ve worked with frameworks like VeeValidate or FormKit, you know they are powerful, but sometimes you just want complete control over markup and styling, while still getting a declarative, maintainable form API.
2. Enter vue-uform
vue-uform is my attempt to solve these pain points.
It’s a unstyled, component-first form library for Vue 3.
Key ideas:
- Component-first API – Fields, forms, and submit buttons are all Vue components.
- No built-in styles – You write your own HTML & CSS or use your favorite UI framework (Element Plus, Naive UI, Vuetify…).
- Composable validation – Built-in rules for common needs, plus an easy API for custom validators.
- f-model directive – A tiny Vite plugin that gives you v-model-like syntax for form fields, without repetitive boilerplate.
3. Quick Start Example
<script setup>
const formValues = { username: '', password: '' }
function doLogin(data) {
console.log('Form submitted:', data)
}
</script>
<template>
<u-form :values="formValues" @submit="doLogin">
<u-field name="username" label="Username" validation="required" v-slot="{ value, update }">
<input f-model />
</u-field>
<u-field name="password" label="Password" validation="required|min:6" v-slot="{ value, update }">
<input type="password" f-model />
</u-field>
<u-submit>Login</u-submit>
</u-form>
</template>
Here, <u-field>
handles:
- Tracking the field’s value
- Running validation rules
- Passing data to the form
The f-model directive is provided by @vue-uform/vite-plugin, which compiles:
<input f-model />
into
<input :value="value" @input="$event => update($event.target.value)" />
4. Built-in Validation Rules
Out of the box, you get:
required, number, email, between, max, min, alpha, alphanumeric, starts_with, ends_with, url, and more.
Example custom validator with parameters:
<script setup>
import { FieldNode } from 'vue-uform'
function minWords(node: FieldNode, min: number) {
const words = String(node.value.value).trim().split(/\s+/).length
return words >= min || `Please enter at least ${min} words.`
}
</script>
<template>
<u-field
name="bio"
validation="min_words:5"
:rules="{ min_words: minWords }"
v-slot="{ value, update }"
>
<textarea f-model></textarea>
</u-field>
</template>
5. Why This Works Well
- UI Freedom – Works equally well with native HTML inputs or from Naive UI, from Element Plus, etc.
- Declarative Structure – The form is defined entirely in the template; minimal JS/TS logic is needed.
- Extensible – Add custom validation logic without hacking the core.
- Consistent API – Every field gets the same slot props (value, update, hasError).
6. Installation
pnpm install vue-uform
pnpm install @vue-uform/vite-plugin -D
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uForm from '@vue-uform/vite-plugin'
export default defineConfig({
plugins: [vue(), uForm()],
})
// main.ts
import { plugin } from 'vue-uform'
createApp(App).use(plugin).mount('#app')
7. Link
GitHub: https://github.com/tu6ge/vue-uform
StackBlitz: https://stackblitz.com/~/github.com/tu6ge/vue-uform-example
Closing Thoughts
If you like the flexibility of writing your own HTML & CSS but want the convenience of declarative form handling and validation, vue-uform might be a good fit.
I’d love to hear your feedback, ideas, and PRs.
Top comments (2)
Good job! Maybe you add themes for css styles?
I also develop ui lib for chats, chotto UI, and Chotto have 3 basic themes. And developer can customize ui with own theme
I think it would be better without styles, as this would allow users more flexibility and freedom in their usage.