vue-modern-form
v1.0.1
Published
Vue 3 form validation library with error components, scroll-to-error, submit activation control, and optional native validation.
Maintainers
Readme
Vue-Modern-Form 😎
A lightweight and flexible form validation plugin for Vue 3.
✔ No schema definitions required — validate using familiar HTML-style rules.
✔ Works seamlessly with native inputs and custom components.
✔ Supports custom validation rules and error messages.
✔ Automatically tracks input elements — no manual IDs or selectors needed.
✔ Small footprint, under 3KB gzipped.
Getting Started
1. Installation
$ npm i vue-modern-form2. Import into your project as Vue Plugin
Vue 3
In /src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import modernForm from 'vue-modern-form'
const app = createApp(App)
app.use(modernForm)
app.mount('#app')Nuxt 3
Create a modernForm.js file under plugins folder with the following content:
import modernForm from 'vue-modern-form'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(modernForm)
})Basic Usage
- Build your form as usual, but wrap it in a
<MForm>component. - Add
<MError>with validation attributes.
<template>
<MForm @submit="submitHandler">
<div>
<label>Name</label>
<input type="text" v-model="name">
<MError v-model="name" required minlength="3"/>
</div>
<div>
<label>Password</label>
<input type="password" v-model="password">
<MError v-model="password" required/>
</div>
<button type="submit">Submit</button>
</MForm>
</template>
<script setup lang="ts">
const submitHandler = (e: ModernSubmitEvent) => {
// You don't have to call e.preventDefault(). It's prevented automatically.
if (!e.isValid) return // stop if form is not valid
// whatever you want to do if form is valid
}
</script>Note: By default, error messages have no styling. You can style them with the ._mf_ErrMsg class.
How It Works
When the v-model in <MError> changes, the value will be validated with its attributes. If the value is invalid, <MError> will render the corresponding error message.
During form submission, <MForm> will pick up the event and trigger all nested <MError> to run validation again.
The isValid property in the submit event indicates whether the form is valid.
Auto Input Detection: <MError> automatically finds its associated input element within the same parent container, eliminating the need for manual id and target attributes in most cases.
Components
<MForm>
Props
| Name | Info | Type | Default | | -- | -- | -- | -- | | native-validate | Enable browser's native form validation. | boolean | false | | focus-error | Auto scroll to invalid input upon form submission. See Example | boolean | false | | focus-offset | The offset position (in pixels) for auto scroll. Useful for fixed headers. See Example. | number | 0 | | activate | Specify when should the validation happen. See Example | string | 'always'
Methods
clearErrors()
Removes all error messages by calling the clear() method on every <MError>.
hasError()
Returns true if there's error in the form.
<MError>
Props
| Name | Info | Type | | -- | -- | -- | | v-model | *Required. The value of your input. | any | | messages | Custom validation messages. See Example. | object | | target | The css selector to select associated html elements. See Example. | string |
Note: The target prop is optional. <MError> automatically detects input elements in the same parent container.
Methods
validate()
Validates the input. If the value is invalid, an error message will be shown. If target prop is provided, matched elements will have .invalid or .valid class.
clear()
Clears the error message. Also removes .invalid and .valid class from matched target.
Built-in Validations
Vue Modern Form provides extensive built-in validations:
Basic Validations
required- Field must have a valueminlength="3"- Minimum character lengthmaxlength="10"- Maximum character lengthexactlength="5"- Exact character length requiredmin="1"- Minimum numeric valuemax="5"- Maximum numeric value
Email & URL Validations
validemail- Valid email formatvalidurl- Valid URL formaturlcond- URL with or without protocolurlcondhttp- URL must start with http:// or https://
Phone & Text Validations
validphone- Valid phone number formatalphanumeric- Only letters and numbersalpha- Only lettersnumeric- Only numbersnowhitespace- No whitespace allowedusername- Valid username (3-20 chars, letters, numbers, underscore, hyphen)
Number Validations
integer- Must be an integerdecimal- Must be a valid decimal numberpositive- Must be positive numbernegative- Must be negative numberdigits="5"- Exact number of digits
Date Validations
validdate- Valid date formatmindate="2024-01-01"- Date must be on or after specified datemaxdate="2024-12-31"- Date must be on or before specified date
Password Validations
strongpassword- 8+ chars with uppercase, lowercase, number, and special characterpasswordcond- Must contain letters, numbers, and special charactersdigitpass- 8+ chars with letters and digitsspecialcasepass- 8+ chars with letters, numbers, and special characterslowercasepass- Must contain uppercase, lowercase, digit, and special characteruppercasepass- Must contain at least one uppercase letter
File Input Validations
accept="image/*"- Validate file types in file inputmaxfile="2"- Maximum number of files allowed Examplemaxsize="5242880"- Maximum file size in bytes (5MB = 5242880 bytes)minsize="1024"- Minimum file size in bytes
Comparison Validations
:equalto="otherState"- Input value must match another value (useful for confirming password) Example:notequalto="otherState"- Input value cannot match another value
Miscellaneous Validations
colorvalidation- Valid hex color code (e.g., #FF5733 or #F57)isFractional- Valid fraction format (e.g., 1/2, 3/4)isAlphanumeric- Only alphanumeric charactersisNegativeAmount- Valid amount with up to 2 decimal places
Configurations
You can modify modernForm's behavior with app.use().
app.use(modernForm, {
... options
})options having the following interface
interface ModernFormOptions {
defaultMessages?: ErrorMessages,
message?: (error: ErrorObject) => string,
customRules?: {
[key: string]: RuleEntry
},
activate?: 'always' | 'first_submit' | 'only_submit' | 'never',
nativeValidate?: boolean
}defaultMessages
Overwrites default validation messages globally.
E.g. Overwriting message for required.
app.use(modernForm, {
defaultMessages: {
required: `Don't be lazy.`
}
})message(error)
The function to generate validation message. The error object has the following properties:
| Parameters | Info |Type | Examples | -- | -- | -- | -- | | type | The failed validation type. | string | "required", "minlength" | value | The expected valid value. | object | {n: 3} *when minlength="3" |
By default, Vue Modern Form will read the type and value.n to generate validation message.
E.g. Translating messages with vue-i18n:
In main.js
const messages = {
en: {
error_required: "This field is required.",
error_minlength: "Please enter at least {n} characters.",
error_maxlength: "Please enter no more than {n} characters.",
error_equalto: "Please enter the same value again.",
error_validemail: "Please enter a valid email address.",
error_min: "The minimum value is {n}.",
error_max: "The maximum value is {n}.",
error_accept: "This file type is not accepted.",
error_maxfile: "Please select no more than {n} files.",
error_maxsize: "The file must be smaller than {n}Mb."
},
de: {
error_required: "Dieses Feld ist erforderlich.",
error_minlength: "Bitte geben Sie mindestens {n} Zeichen ein.",
error_maxlength: "Bitte geben Sie nicht mehr als {n} Zeichen ein.",
error_equalto: "Bitte geben Sie denselben Wert erneut ein.",
error_validemail: "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
error_min: "Der Mindestwert ist {n}.",
error_max: "Der Höchstwert ist {n}.",
error_accept: "Dieser Dateityp wird nicht akzeptiert.",
error_maxfile: "Bitte wählen Sie nicht mehr als {n} Dateien aus.",
error_maxsize: "Die Datei muss kleiner als {n}Mb sein."
}
}
const i18n = createI18n({
locale: 'en',
fallbackLocale: 'en',
messages
})
app.use(modernForm, {
message(error) {
const translateKey = `error_${error.type}`
return error.value?.n
? i18n.global.t(translateKey, { n: error.value.n })
: i18n.global.t(translateKey)
}
})customRules
Add custom validation rules.
Entries for customRules must satisfy this interface
interface RuleEntry {
rule: (val: any, validateValue?: any) => ValidationResponse,
message: string,
auto?: boolean
}E.g. Add a custom attribute that checks whether input value is a multiple of 3.
<input type="number" v-model="answer">
<MError v-model="answer" multipleof="3" />app.use(modernForm, {
customRules: {
multipleof: {
rule(val, validateValue) {
// val is your input's value,
// validateValue is the value you passed in the attribute, in this case, 3
if (Number(val) % validateValue != 0) { // condition for invalid value
// must return an object with 'type' key
return {
type: 'multipleof',
value: {
n: validateValue
}
}
}
},
message: 'Value must be multiple of {n}',
}
}
})Note: the auto property in RuleEntry. When set to true, MError will automatically run validation when the value specified in attribute changes. This is useful when the validation involves other dynamic states. The built-in validations "equalto" and "notequalto" are set to auto:true. See example
activate
Globally set the default value of activate prop for <MForm>.
nativeValidate
Globally set the default value of native-validate prop for <MForm>.
Examples
Custom validation message
Use messages prop to show custom validation messages.
<template>
...
<MError v-model="name" required minlength="2" :messages="customMessage" />
</template>
<script setup>
const customMessage = {
required: 'Name is required',
}
// since minlength is not specified in 'customMessage', it will use the default validation message
</script>Adding class to inputs
Use the target prop on <MError> as css selector to select elements. Selected elements will have .invalid class added when the input is invalid, .valid when valid.
Note: In most cases, you don't need to specify target as <MError> automatically detects input elements in the same parent container.
<input type="email" id="emailInput" v-model="name"/>
<MError v-model="name" required validemail target="#emailInput" />Enabling browser's default validation
Simply add a native-validate attribute on <MForm>
<MForm @submit="submitHandler" native-validate>
<!-- Your inputs -->
</MForm>Scroll to invalid input
By adding focus-error prop on <MForm>, invalid inputs are automatically scrolled into view upon form submission. This is useful when you have a long form.
<MForm @submit="handleSubmit" focus-error>
<div>
<input type="text" v-model="name" />
<MError v-model="name" required />
</div>
</MForm>Note: <MError> automatically detects the associated input element in the same parent container, so you don't need to add id or target attributes.
Offsetting scroll
We can offset the scroll position by using focus-offset. This is useful if you have a floating header that covers the input after scrolling.
<MForm @submit="handleSubmit" focus-error :focus-offset="90">This will offset the scroll position by 90 pixels, ensuring the error field isn't hidden behind a fixed header.
When to validate
The activate prop controls the validation behavior. The value could be:
- "always" - Validate every time the v-model changes. (default)
- "first_submit" - Only start to validate on the first form submission, and then behave like "always".
- "only_submit" - Only validate during form submissions.
- "never" - Disable validation.
e.isValidfrom the submit event will always betrue
<MForm activate="first_submit">Clearing Form Errors
Call the clearErrors() method on <MForm> to clear all errors.
<template>
<MForm ref="formRef">
<!-- ...your inputs -->
<button type="reset" @click="clearForm">Reset</button>
</MForm>
</template>
<script setup>
const formRef = ref(null)
const clearForm = (e) => {
formRef.value.clearErrors()
}
</script>File Input Validations
<template>
<input type="file" multiple @change="fileChange">
<MError v-model="file" maxsize="2097152" maxfile="2" required />
</template>
<script setup>
const file = ref('')
const fileChange = (e) => {
file.value = e.target.files
}
</script>Password And Confirm Password
<div>
<label>Password</label>
<input type="password" v-model="password">
<MError v-model="password" required />
</div>
<div>
<label>Confirm Password</label>
<input type="password" v-model="confirmPassword" />
<MError v-model="confirmPassword" :equalto="password" />
</div>Key Features
Auto Input Detection
<MError> automatically finds its associated input element within the same parent container. This eliminates the need for manual id and target attributes in most cases, making your code cleaner and easier to maintain.
<div>
<input type="email" v-model="email">
<MError v-model="email" required validemail />
</div>Smooth Scrolling
When focus-error is enabled, the form smoothly scrolls to the first invalid input field, providing a better user experience.
Flexible Validation
Choose when validation occurs using the activate prop - perfect for different UX patterns.
Extensive Validation Rules
Over 40 built-in validation rules covering common use cases, from basic required fields to complex password requirements.
Made by singso 😊
