Building forms in Vue.js doesn't have to be complicated. Whether you're a beginner just getting started with Vue forms or an experienced developer looking to streamline your form development process, this tutorial will walk you through creating your first dynamic form with Enforma from start to finish.
By the end of this guide, you'll have built a complete user registration form with validation, nested fields, and repeatable sections—all with minimal boilerplate code.
What We'll Build
In this tutorial, we'll create a comprehensive user registration form that includes:
- Basic field validation (name, email, password)
- Cross-field validation (password confirmation)
- Nested data handling (address information)
Step 1: Installing and Setting Up Enforma
First, let's install Enforma in your Vue 3 project:
npm install @encolajs/enforma
Next, set up the Enforma plugin in your main application file:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { EnformaPlugin } from '@encolajs/enforma'
import useDefaultPreset from '@encolajs/presets/default'
const app = createApp(App)
app.use(EnformaPlugin)
app.mount('#app')
// Enforma is UI agnostic
// The default preset lets you use the HTML input fields
useDefaultPreset()
That's it! Enforma is now ready to use in your Vue application.
Optional: UI Framework Integration
If you're using a UI framework like PrimeVue, Vuetify, or Quasar, you can use Enforma's built-in presets for seamless integration:
// For PrimeVue users
import { usePrimeVuePreset } from '@encolajs/enforma/presets/primevue'
usePrimeVuePreset()
// For Vuetify users
import { useVuetifyPreset } from '@encolajs/enforma/presets/vuetify'
useVuetifyPreset()
// For Quasar users
import { useQuasarPreset } from '@encolajs/enforma/presets/quasar'
useQuasarPreset()
Step 2: Creating Your First Simple Form
Let's start with a basic contact form to understand Enforma's fundamentals:
<template>
<div class="max-w-md mx-auto p-6">
<h2 class="text-2xl font-bold mb-6">Contact Us</h2>
<Enforma
:data="formData"
:rules="validationRules"
:submit-handler="handleSubmit"
>
<EnformaField
name="name"
label="Full Name"
required
/>
<EnformaField
name="email"
label="Email Address"
required
/>
<EnformaField
name="message"
label="Message"
input-component="textarea"
required
/>
<template #actions>
<EnformaSubmitButton class="w-full bg-blue-500 text-white py-2 px-4 rounded">
Send Message
</EnformaSubmitButton>
</template>
</Enforma>
</div>
</template>
<script setup>
import { Enforma, EnformaField, EnformaSubmitButton } from '@encolajs/enforma'
const formData = {
name: '',
email: '',
message: ''
}
const validationRules = {
name: 'required',
email: 'required|email',
message: 'required|min:10'
}
const handleSubmit = async (data) => {
console.log('Form submitted:', data)
// Here you would typically send the data to your API
return true
}
</script>
Step 3: Adding Advanced Validation
Now let's enhance our form with more sophisticated validation rules and custom error messages:
<template>
<div class="max-w-md mx-auto p-6">
<h2 class="text-2xl font-bold mb-6">Create Account</h2>
<Enforma
:data="formData"
:rules="validationRules"
:custom-messages="customMessages"
:submit-handler="handleSubmit"
>
<EnformaField name="username" label="Username" required />
<EnformaField name="email" label="Email Address" required />
<EnformaField
name="password"
label="Password"
input-component="password"
required
/>
<EnformaField
name="password_confirmation"
label="Confirm Password"
input-component="password"
required
/>
<EnformaField name="age" label="Age" input-component="number" required />
<template #actions>
<EnformaSubmitButton>Create Account</EnformaSubmitButton>
</template>
</Enforma>
</div>
</template>
<script setup>
const formData = {
username: '',
email: '',
password: '',
password_confirmation: '',
age: null
}
const validationRules = {
username: 'required|min:3|max:20',
email: 'required|email',
password: 'required|min:8',
password_confirmation: 'required|same:@password',
age: 'required|number|gte:18'
}
const customMessages = {
'username.required': 'Please choose a username',
'username.min': 'Username must be at least 3 characters',
'email.email': 'Please enter a valid email address',
'password.min': 'Password must be at least 8 characters',
'password_confirmation.same': 'Passwords must match',
'age.gte': 'You must be at least 18 years old'
}
const handleSubmit = async (data) => {
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('Account created:', data)
return true
} catch (error) {
console.error('Failed to create account:', error)
return false
}
}
</script>
Step 4: Handling Nested Fields
Let's add address information to demonstrate how Enforma handles nested data structures:
<template>
<div class="max-w-md mx-auto p-6">
<h2 class="text-2xl font-bold mb-6">User Profile</h2>
<Enforma :data="formData" :rules="validationRules">
<!-- Personal Information -->
<div class="mb-6">
<h3 class="text-lg font-semibold mb-3">Personal Information</h3>
<EnformaField name="personal.firstName" label="First Name" required />
<EnformaField name="personal.lastName" label="Last Name" required />
<EnformaField name="personal.email" label="Email" required />
</div>
<!-- Address Information -->
<div class="mb-6">
<h3 class="text-lg font-semibold mb-3">Address</h3>
<EnformaField name="address.street" label="Street Address" required />
<EnformaField name="address.city" label="City" required />
<EnformaField name="address.zipCode" label="ZIP Code" required />
<EnformaField name="address.country" label="Country" required />
</div>
<EnformaSubmitButton>Save Profile</EnformaSubmitButton>
</Enforma>
</div>
</template>
<script setup>
const formData = {
personal: {
firstName: '',
lastName: '',
email: ''
},
address: {
street: '',
city: '',
zipCode: '',
country: ''
}
}
const validationRules = {
'personal.firstName': 'required',
'personal.lastName': 'required',
'personal.email': 'required|email',
'address.street': 'required',
'address.city': 'required',
'address.zipCode': 'required|min:5',
'address.country': 'required'
}
</script>
What's Next?
Congratulations! You've successfully built your first dynamic Vue form with Enforma. You've learned how to:
✅ Install and set up Enforma in a Vue project
✅ Create forms with validation
✅ Handle nested data structures
Next Steps
Now that you've mastered the basics, consider exploring:
- Repeatable fields for collections of fields
- Schema-based forms for even more dynamic form generation
- Headless components for complete UI control
- Custom validation rules for complex business logic
- UI framework integration with PrimeVue, Vuetify, or Quasar
- Advanced features like conditional fields and transformers
Ready to dive deeper? Check out the Enforma documentation for advanced tutorials and comprehensive API references.
Complete Example Code
Here's the complete code for a comprehensive registration form combining all the concepts we've covered:
<template>
<div class="max-w-2xl mx-auto p-6">
<h1 class="text-3xl font-bold mb-8">Complete Registration</h1>
<Enforma
:data="formData"
:rules="validationRules"
:custom-messages="customMessages"
:submit-handler="handleSubmit"
>
<!-- Personal Info -->
<div class="mb-8">
<h2 class="text-xl font-semibold mb-4">Personal Information</h2>
<div class="grid grid-cols-2 gap-4">
<EnformaField name="personal.firstName" label="First Name" required />
<EnformaField name="personal.lastName" label="Last Name" required />
</div>
<EnformaField name="personal.email" label="Email" required />
<EnformaField name="personal.password" label="Password" input-component="password" required />
<EnformaField name="personal.confirmPassword" label="Confirm Password" input-component="password" required />
</div>
<!-- Address -->
<div class="mb-8">
<h2 class="text-xl font-semibold mb-4">Address</h2>
<EnformaField name="address.street" label="Street Address" required />
<div class="grid grid-cols-2 gap-4">
<EnformaField name="address.city" label="City" required />
<EnformaField name="address.zipCode" label="ZIP Code" required />
</div>
</div>
</Enforma>
</div>
</template>
<script setup>
import { Enforma, EnformaField, EnformaRepeatable, EnformaSubmitButton } from '@encolajs/enforma'
const formData = {
personal: {
firstName: '',
lastName: '',
email: '',
password: '',
confirmPassword: ''
},
address: {
street: '',
city: '',
zipCode: ''
},
skills: [{ name: '', level: '' }]
}
const validationRules = {
'personal.firstName': 'required',
'personal.lastName': 'required',
'personal.email': 'required|email',
'personal.password': 'required|min:8',
'personal.confirmPassword': 'required|same:@personal.password',
'address.street': 'required',
'address.city': 'required',
'address.zipCode': 'required|min:5',
'skills.*.name': 'required',
'skills.*.level': 'required'
}
const customMessages = {
'personal.confirmPassword.same': 'Passwords must match',
'personal.password.min': 'Password must be at least 8 characters',
'address.zipCode.min': 'ZIP code must be at least 5 characters'
}
const handleSubmit = async (data) => {
console.log('Registration data:', data)
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000))
alert('Account created successfully!')
return true
}
</script>
Happy form building! 🚀
Hi! claim your exclusive around $15 in BNB-based tokens ending soon! — Start your web3 journey! Connect your wallet to get the bonus. 👉 duckybsc.xyz