A Step-by-Step Guide to Building Your First Vue Form with Enforma
Adrian Miu

Adrian Miu @adrian_miu

Joined:
May 24, 2025

A Step-by-Step Guide to Building Your First Vue Form with Enforma

Publish Date: Jun 11
0 1

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
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Happy form building! 🚀

Comments 1 total

  • Richard
    RichardJun 11, 2025

    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

Add comment