Form Builder is a flexible Vue 3 component that works with Inertia's useForm and shadcn/ui components to create dynamic forms with minimal configuration.
Ensure you have the required shadcn/ui components installed:
php artisan shadcn:init
php artisan shadcn:component button
php artisan shadcn:component input
php artisan shadcn:component label
php artisan shadcn:component select
php artisan shadcn:component checkbox
php artisan shadcn:component textarea
php artisan shadcn:component cardHere's a simple example of how to use the Form Builder:
<script setup>
import { useForm } from '@inertiajs/vue3'
import { createSchema, FormBuilder } from '@/Components/FormBuilder'
const form = useForm({
name: '',
email: '',
message: ''
})
const schema = createSchema()
.field('name')
.required()
.placeholder('Your name')
.field('email')
.email()
.required()
.placeholder('your@email.com')
.field('message')
.textarea()
.rows(6)
.placeholder('Your message here')
.build()
</script>
<template>
<FormBuilder
:form="form"
:schema="schema"
title="Contact Form"
@submit="form.post('/contact')"
/>
</template>The schema builder provides a fluent API to configure form fields:
const schema = createSchema()
.field('username')
.label('Username')
.required()
.props({
autocomplete: 'username'
})
.field('email')
.email()
.label('Email Address', {
required: true,
requiredText: '(Required)',
className: 'font-semibold'
})
.field('country')
.select([
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' }
])
.field('bio')
.textarea()
.rows(4)
.span('full')
.asCard()
.build()- Text Input:
.field('username')
.text() // default type
.placeholder('Enter username')- Email:
.field('email')
.email()
.required()- Textarea:
.field('description')
.textarea()
.rows(4)- Select:
.field('country')
.select([
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' }
])- Checkbox:
.field('terms')
.checkbox()
.props({ label: 'I agree to terms' })You can customize how fields are rendered using slots:
This will apply to all fields of a specific type:
<FormBuilder :form="form" :schema="schema">
<!-- Custom component for all text inputs -->
<template #text="{ field, value, onChange }">
<Input
:id="field.name"
:value="value"
@input="e => onChange(e.target.value)"
v-bind="field.props"
/>
</template>
</FormBuilder>This will only apply to a specific field:
<FormBuilder :form="form" :schema="schema">
<!-- Custom component just for email field -->
<template #field-email="{ field, value, onChange }">
<Input
:id="field.name"
:value="value"
@input="e => onChange(e.target.value)"
class="special-email-input"
type="email"
v-bind="field.props"
/>
</template>
</FormBuilder>- Field Type Slots:
#text- Text inputs#textarea- Textarea fields#select- Select dropdowns#checkbox- Checkbox inputs
- Field-Specific Slots:
#field-{fieldName}- Custom component for specific field
- General Slots:
#label- Customize field labels#help- Help text below fields#errors- Error messages#submit- Submit button
Each slot receives these props:
field- Field configurationvalue- Current field valueonChange- Function to update valueform- The entire form object
Control the form layout using columns:
<FormBuilder :form="form" :schema="schema" :columns="2">Span fields across columns:
.field('email')
.span(2) // Takes up 2 columns
.required()
.field('bio')
.span('full') // Takes full width
.textarea()Wrap fields in cards:
.field('profile')
.asCard()
.span('full')Or wrap the entire form:
<FormBuilder :form="form" :schema="schema" :useCards="true">Customize the submit button:
<FormBuilder :form="form" :schema="schema">
<template #submit="{ form }">
<div class="flex justify-end space-x-2">
<Button variant="outline">Cancel</Button>
<Button type="submit" :disabled="form.processing">
{{ form.processing ? 'Saving...' : 'Save Changes' }}
</Button>
</div>
</template>
</FormBuilder>Customize error display:
<FormBuilder :form="form" :schema="schema">
<template #errors="{ errors }">
<div v-if="Object.keys(errors).length" class="bg-red-50 p-4 rounded-md">
<ul class="list-disc pl-4">
<li v-for="error in errors" :key="error" class="text-red-700">
{{ error }}
</li>
</ul>
</div>
</template>
</FormBuilder>- Always provide meaningful labels for fields
- Use appropriate field types
- Group related fields using grid layout
- Use cards for complex sections
- Provide clear validation messages
- Consider mobile responsiveness when choosing layouts
- Use help text for complex fields
- Customize components to match your design system