DEV Community

Cover image for Creating Dynamic Forms with Laravel and Vue 3
Abodh Kumar for AddWeb Solution Pvt Ltd

Posted on • Edited on

Creating Dynamic Forms with Laravel and Vue 3

Static forms limit your application’s flexibility; dynamic forms unlock infinite possibilities.- Addy Osmani

Dynamic forms adapt to your data and business logic, generating fields, validation, and layouts automatically. If you're building admin panels, multi-step wizards, or configurable forms for club management systems, Laravel + Vue 3 Composition API delivers the perfect stack for clean, scalable form experiences.
This guide walks you through creating truly dynamic forms that render from JSON schemas, handle real-time validation, and integrate seamlessly with Laravel's backend validation and API endpoints.

Key Takeaways

  • Use JSON schema definitions to describe form structure from Laravel backend
  • Leverage Vue 3 Composition API for reactive field management and dynamic rendering
  • Implement Laravel Precognition for real-time validation without frontend duplication
  • Use Filament or custom components for rapid prototyping of complex forms
  • Handle nested fields, conditional visibility, and multi-step wizards with computed properties
  • Dynamic forms make your Laravel apps flexible, maintainable, and user-friendly

Index

  1. Why Dynamic Forms Matter
  2. How Laravel + Vue 3 Dynamic Forms Work
  3. Schema-Driven vs Component-Driven Forms
  4. Setting Up Dynamic Forms in Laravel + Vue 3
  5. Creating Form Schema from Laravel
  6. Building Reactive Form Components in Vue 3
  7. Real-time Validation with Laravel Precognition
  8. Handling Conditional Fields and Nested Forms
  9. Advanced Patterns: Multi-step Wizards
  10. Common Mistakes to Avoid
  11. FAQs
  12. Interesting Facts & Stats
  13. Conclusion

1. Why Dynamic Forms Matter

"Static forms limit your app's flexibility; dynamic forms unlock infinite possibilities."

Dynamic forms generate UI based on data structure, eliminating hardcoded inputs and enabling:

  1. Admin panels: Adapt to any database schema or user permissions
  2. Multi-step wizards: Complex club/event registrations with flexible steps
  3. Configurable forms: Different user roles or entities with role-specific fields
  4. Real-time validation: Synced with Laravel backend rules instantly
  5. Rapid prototyping: No frontend-backend coordination delays
  6. Laravel excels at generating form schemas from models/migrations, while Vue 3's Composition API makes reactive field management effortless.

2. How Laravel + Vue 3 Dynamic Forms Work

Dynamic forms revolve around:

  • Form Schema: JSON describing field types, validation, labels, options
  • Laravel Backend: Controllers generate schemas from models/DB structure
  • Vue 3 Frontend: Composition API renders fields reactively with v-model
  • Real-time Validation: Laravel Precognition or API calls for live feedback

Example 98km lkbn m /:
// Laravel returns schema + initial data

return response()->json([
     'schema' => $formSchema,
     'data' => $modelData
]);
import { ref, reactive, computed } from 'vue';
    const formData = ref({});
    const schema = ref([]);
    const dynamicFields = computed(() =>
    schema.value.map(field => ({
    ...field,
    value: formData.value[field.name]
    }))
);
Enter fullscreen mode Exit fullscreen mode

Vue 3's reactivity ensures the entire form updates instantly when schema changes.

3. Schema-Driven vs Component-Driven Forms

Table 1: Comparison of Schema-Driven and Component-Driven Forms
Schema-driven wins for enterprise apps where forms change frequently based on data models or user roles.

4. Setting Up Dynamic Forms in Laravel + Vue

Laravel setup:
composer require filament/forms-component
npm install vue@3 @vue/compiler-sfc axios
Core configuration:
Laravel routes return JSON schema + model data
Vue 3 app uses Composition API with reactive refs
Inertia.js or SPA setup for seamless integration
Basic route structure:

Route::get('/forms/{model}', function($model) {
  schema=generateSchemaFromModel(model);
  data=getModelData(model);
  return inertia('DynamicForm', compact('schema', 'data'));
  });
Enter fullscreen mode Exit fullscreen mode

Vue 3 app initialization with reactive form state.

The best architectures remove duplication and centralize decision-making. - Martin Fowler

5. Creating Form Schema from Laravel

Generate schemas dynamically from models, migrations, or DB structure:

class FormSchemaGenerator
{
   public static function fromModel(string $model): array
   {
       $fields = [];
       $columns = Schema::getColumnListing((new                    $model)->getTable());
       foreach ($columns as $column) {
           $fields[] = [
               'name' => $column,
               'type' => self::getFieldType($column),
               'label' => ucwords(str_replace('_', ' ', $column)),
               'rules' => self::getValidationRules($column),
               'options' => $column === 'status' ? ['active', 'inactive'] : null,
           ];
       }


       return $fields;
   }

private static function getFieldType(string $column): string
   {
       if (str_contains($column, 'email'))
           return 'email';
       if (str_contains($column, 'phone'))
           return 'tel';
       if (str_contains($column, 'date'))
           return 'date';
       if (str_contains($column, 'password'))
           return 'password';
       return 'text';
   }
}
Enter fullscreen mode Exit fullscreen mode

Controller example:

public function edit($id)
{
   model=ClubEvent::find(id);
   $schema = FormSchemaGenerator::fromModel(ClubEvent::class);
   return inertia('EditForm', [
       'schema' => $schema,
       'data' => $model->toArray()
   ]);
}
Enter fullscreen mode Exit fullscreen mode

This approach auto-generates forms for any model without frontend changes.

6. Building Reactive Form Components in Vue 3

Vue 3 Composition API makes dynamic field rendering clean and reactive:
{{ field.label }}
{{ errors[field.name][0] }}
Submit
Each field type (TextField, SelectField, DateField) becomes a reusable component.

7. Real-time Validation with Laravel Precognition

Laravel Precognition provides live validation powered by your backend rules:
// Vue 3 with Precognition;

import { usePrecognition } from '@inertiajs/vue3'
import { watch } from 'vue';
const form = usePrecognition('post', '/forms/validate');
watch(() => formData.value, () => {
form.validate('email', 'name');
}, { deep: true, throttle: 500 });
Backend route with Precognition:
Route::precognition('forms/validate', function (ValidateFormRequest $request) {
return response()->json(['valid' => true]);
});
Enter fullscreen mode Exit fullscreen mode

Validation errors populate reactively, providing instant feedback without page refresh.

8. Handling Conditional Fields and Nested Forms

Computed properties make conditional fields reactive:

const computedSchema = computed(() =>
   props.schema.map(field => ({...field,
   visible: evaluateCondition(field.condition, formData.value)
   }))
   .filter(field => field.visible !== false)
   );
const evaluateCondition = (condition, data) => {
   if (!condition) return true;
   try {
       return Function(...Object.keys(data), return ${condition.when})(
       ...Object.values(data)
       );
   } catch {
       return true;
   }
};
{
   name: 'address',
   type: 'object',
   fields: [
   { name: 'street', type: 'text', label: 'Street Address' },
   { name: 'city', type: 'select', options: ['NYC', 'LA'], label: 'City' }
   ]
}

Enter fullscreen mode Exit fullscreen mode

Vue 3's reactivity handles deep nested updates automaticallys.

Good software design reduces the cost of change.- Kent Beck

9. Advanced Patterns: Multi-step Wizards

Break complex forms into wizard steps:

import { ref, computed } from 'vue';
   const currentStep = ref(0);
   const formData = ref({});
   const chunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) },
   (_, i) => arr.slice(i * size, i * size + size));
   const steps = computed(() => chunk(props.schema, 5));
   const currentStepFields = computed(() => steps.value[currentStep.value] || []);
   const validateStep = (stepIndex) => {
   const stepFields = steps.value[stepIndex];
   return stepFields.every(field => {
   if (field.required && !formData.value[field.name]) {
       errors.value[field.name] = ['This field is required'];
       return false;
   }
       return true;
       });
   };
   const nextStep = () => {
       if (validateStep(currentStep.value)) {
           if (currentStep.value < steps.value.length - 1) {
               currentStep.value++;
           } else {
               submitForm();
           }
       }
   };
   const previousStep = () => {
       if (currentStep.value > 0) {
           currentStep.value--;
       }
   };
Enter fullscreen mode Exit fullscreen mode

Progress indicator + conditional navigation creates smooth user flows for long forms like event registrations.

10. Common Mistakes to Avoid

  • Storing schema in Vue state instead of backend (loses data-driven benefits)
  • Using Options API for complex dynamic forms (Composition API is superior)
  • Ignoring reactivity cleanup for dynamic refs (memory leaks)
  • Duplicating validation rules between frontend/backend
  • Hardcoding field components instead of dynamic rendering
  • Not debouncing real-time validation (server load)

11. FAQs

Q. When should I use dynamic forms vs static forms?
Use dynamic for admin panels, CMS, configurable UIs; static for simple, performance-critical forms.

Q. Do I need Filament or Livewire?
No. Pure Laravel API + Vue 3 SPA works perfectly. Filament accelerates prototyping.

Q. How do I handle file uploads in dynamic forms?
Use dynamic file field components with Laravel's file validation and temporary uploads.

Q. What's better: JSON Schema or custom format?
Custom format is simpler for Laravel apps; JSON Schema if you need standard compliance.

Q. Can I use Tailwind with dynamic forms?
Yes, pass Tailwind classes through schema for full styling control.

12. Interesting Facts & Stats

  • Laravel Filament's form builder powers thousands of production admin panels with its schema-driven approach.
  • Vue 3 Composition API adoption grew 300%+ since launch for complex form scenarios.
  • Laravel Precognition eliminates frontend validation duplication entirely.
  • Dynamic forms reduce frontend code by 70%+ compared to hardcoded components.
  • Schema-based form generation is the industry standard for headless CMS and enterprise platforms.

13. Conclusion

Dynamic forms with Laravel + Vue 3 transform rigid UIs into adaptive experiences that grow with your data models. By generating schemas from Laravel, rendering reactively in Vue 3 Composition API, and validating in real-time with Precognition, you build maintainable forms that handle any complexity. Perfect for club management systems, admin panels, or any app needing flexible data entry.

References

[1] Vue School. (2025). The Ultimate Guide for Using Vue.js with Laravel. https://vueschool.io/articles/vuejs-tutorials/the-ultimate-guide-for-using-vue-js-with-laravel/
[2] Digital Patio. (2024). Build Better Forms with Vue.js 3 Composition API. https://digitalpatio.hashnode.dev/build-better-forms-with-vuejs-3-composition-api-a-practical-guide
[3] Dev.to. (2025). Vue - Build Dynamic Reactive Form. https://dev.to/anirbmuk/vue-build-dynamic-reactive-form-141g
[4] Leighton. (2026). Build a Dynamic Form Component with Vue 3 (Composition API). https://www.leighton.com/insights/build-a-dynamic-form-component-with-vue
[5] Dev.to. (2024). Laravel and Vue.js: How to Display Validation Errors. https://laraveldaily.com/post/laravel-vue-how-to-display-validation-errors
[6] GitHub. (2023). Vue 3 Schema Forms. https://github.com/MaciejDybowski/vue3-schema-forms

About the Author: Abodh is a PHP and Laravel Developer at AddWeb Solution, skilled in MySQL, REST APIs, JavaScript, Git, and Docker for building robust web applications.

Top comments (1)

Collapse
 
martin_miles_297f74dd4964 profile image
Martin Miles

nice approach, especially keeping the form structure in JSON and rendering it in Vue, makes things much easier to scale. One thing though, once you add conditional logic or nested sections, managing that across backend + frontend can get heavy

something like SurveyJS keeps structure + logic in one schema, which simplifies that a bit