gazz96/form-builder

A powerful Laravel form builder that allows you to create forms from array or JSON configuration with support for multiple CSS frameworks (Bootstrap 4, Bootstrap 5, Tailwind CSS)

Maintainers

Package info

github.com/gazz96/ui-core

Homepage

pkg:composer/gazz96/form-builder

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-05-29 04:53 UTC

This package is auto-updated.

Last update: 2026-05-29 04:54:48 UTC


README

A powerful and flexible Laravel package for creating HTML forms from array or JSON configuration. Support for multiple CSS frameworks (Bootstrap, Tailwind) with a fluent, intuitive API.

Features

  • 🎨 Multiple CSS Frameworks - Bootstrap 4, Bootstrap 5, Tailwind, or custom CSS frameworks
  • 📋 Array/JSON Configuration - Define forms declaratively using arrays or JSON
  • 🔄 Fluent API - Chainable methods for easy form building
  • 🎯 Form Fields - Support for all standard HTML5 input types (15+ types)
  • Validation Integration - Extract validation rules directly from field configuration
  • 🎪 Field Grouping - Organize fields with fieldsets and rows
  • 📦 Zero Dependencies - Only requires PHP 8.1+
  • 🔧 Extensible - Create custom field types easily
  • 🅱️ Bootstrap Support - Full support for Bootstrap 4 and Bootstrap 5

Installation

composer require gazz96/form-builder

Laravel will auto-register the service provider.

Quick Start

Using Fluent API (Existing Method)

use BagasTopati\UiCore\UI;

$form = UI::form('/users/store')
    ->text('name', 'Full Name')
    ->email('email', 'Email Address')
    ->password('password', 'Password')
    ->submit('Create User')
    ->render();

Using Array Configuration (New)

use BagasTopati\UiCore\Builders\FormBuilder;

$config = [
    'action' => '/users/store',
    'method' => 'POST',
    'attributes' => [
        'class' => 'user-form',
        'id' => 'user-form'
    ],
    'fields' => [
        [
            'type' => 'text',
            'name' => 'name',
            'label' => 'Full Name',
            'placeholder' => 'Enter your full name',
            'validation' => 'required|string|max:255'
        ],
        [
            'type' => 'email',
            'name' => 'email',
            'label' => 'Email Address',
            'validation' => 'required|email'
        ],
        [
            'type' => 'password',
            'name' => 'password',
            'label' => 'Password',
            'validation' => 'required|min:8'
        ]
    ],
    'buttons' => [
        [
            'type' => 'submit',
            'label' => 'Create User'
        ]
    ]
];

$form = FormBuilder::fromArray($config);
echo $form->render();

Using JSON Configuration

$json = '{
    "action": "/users/store",
    "method": "POST",
    "fields": [
        {
            "type": "text",
            "name": "name",
            "label": "Full Name",
            "validation": "required|string"
        }
    ]
}';

$form = FormBuilder::fromJson($json);
echo $form->render();

Configuration

Publish the configuration file:

php artisan vendor:publish --tag=form-builder-config

Edit config/form-builder.php:

return [
    'default_framework' => env('FORM_BUILDER_FRAMEWORK', 'tailwind'),
    'frameworks' => [
        'bootstrap' => BagasTopati\UiCore\CssFrameworks\BootstrapFramework::class,
        'tailwind' => BagasTopati\UiCore\CssFrameworks\TailwindFramework::class,
        'default' => BagasTopati\UiCore\CssFrameworks\DefaultFramework::class,
    ],
    'custom_field_types' => [],
    'validation' => [
        'include_html5_validation' => true,
    ],
];

Supported Field Types

Input Fields

  • text - Text input
  • email - Email input
  • password - Password input
  • number - Number input
  • tel - Telephone input
  • url - URL input
  • date - Date input
  • time - Time input
  • datetime - DateTime input
  • color - Color picker
  • range - Range slider
  • search - Search input
  • hidden - Hidden input

Complex Fields

  • textarea - Multi-line text
  • select - Dropdown selection
  • checkbox - Single checkbox
  • radio - Radio button group
  • file - File upload

Special Fields

  • group - Fieldset grouping
  • row - Multi-column layout

Field Configuration

Each field can have the following properties:

[
    'type' => 'text',                    // Field type (required)
    'name' => 'field_name',              // Field name (required)
    'label' => 'Field Label',            // Display label
    'placeholder' => 'Enter value',      // Placeholder text
    'default' => 'default_value',        // Default value
    'validation' => 'required|email',    // Laravel validation rules
    'required' => true,                  // HTML5 required attribute
    'attributes' => [                    // Custom HTML attributes
        'class' => 'custom-class',
        'data-custom' => 'value'
    ]
]

Validation Rules

Extract validation rules from form configuration:

$form = FormBuilder::fromArray($config);
$rules = $form->getValidationRules();
// ['name' => 'required|string|max:255', 'email' => 'required|email']

// Use with Laravel validator
$validated = validator($data, $rules)->validate();

Form Export

Export to Array

$form = FormBuilder::fromArray($config);
$array = $form->toArray();

Export to JSON

$json = $form->toJson();
// Or with options
$json = $form->toJson(JSON_PRETTY_PRINT);

Advanced Examples

Select Field with Options

[
    'type' => 'select',
    'name' => 'role',
    'label' => 'User Role',
    'options' => [
        'admin' => 'Administrator',
        'editor' => 'Editor',
        'viewer' => 'Viewer'
    ],
    'default' => 'viewer',
    'validation' => 'required|in:admin,editor,viewer'
]

Grouped Fields

[
    'type' => 'group',
    'label' => 'Personal Information',
    'fields' => [
        [
            'type' => 'text',
            'name' => 'first_name',
            'label' => 'First Name'
        ],
        [
            'type' => 'text',
            'name' => 'last_name',
            'label' => 'Last Name'
        ]
    ]
]

Row Layout (Multi-Column)

[
    'type' => 'row',
    'fields' => [
        [
            'type' => 'text',
            'name' => 'first_name',
            'label' => 'First Name'
        ],
        [
            'type' => 'text',
            'name' => 'last_name',
            'label' => 'Last Name'
        ]
    ]
]

File Upload with Validation

[
    'type' => 'file',
    'name' => 'avatar',
    'label' => 'Profile Picture',
    'accept' => 'image/*',
    'validation' => 'required|image|max:2048'
]

CSS Framework Switching

FormBuilder supports multiple CSS frameworks with different Bootstrap versions!

Supported Frameworks

  • bootstrap - Bootstrap (default)
  • bootstrap4 - Bootstrap 4.x
  • bootstrap5 - Bootstrap 5.x
  • tailwind - Tailwind CSS
  • default - Generic CSS

In Configuration

// config/form-builder.php
'default_framework' => env('FORM_BUILDER_FRAMEWORK', 'bootstrap5'),

'frameworks' => [
    'bootstrap' => Bootstrap Framework::class,
    'bootstrap4' => Bootstrap4Framework::class,
    'bootstrap5' => Bootstrap5Framework::class,
    'tailwind' => TailwindFramework::class,
    'default' => DefaultFramework::class,
]

At Runtime

use BagasTopati\UiCore\UI;

// Use Bootstrap 4
UI::setFramework(new \BagasTopati\UiCore\CssFrameworks\Bootstrap4Framework());

// or Bootstrap 5
UI::setFramework(new \BagasTopati\UiCore\CssFrameworks\Bootstrap5Framework());

// or via environment variable
UI::useBootstrap(); // Uses default framework

$form = FormBuilder::fromArray($config);
echo $form->render();

Environment Variable

# .env
FORM_BUILDER_FRAMEWORK=bootstrap5

Real-World Example

// Store user form configuration
$userFormConfig = [
    'action' => route('users.store'),
    'method' => 'POST',
    'attributes' => [
        'class' => 'user-registration-form',
        'novalidate' => true
    ],
    'fields' => [
        [
            'type' => 'text',
            'name' => 'name',
            'label' => 'Full Name',
            'placeholder' => 'John Doe',
            'validation' => 'required|string|max:255'
        ],
        [
            'type' => 'email',
            'name' => 'email',
            'label' => 'Email Address',
            'placeholder' => 'john@example.com',
            'validation' => 'required|email|unique:users'
        ],
        [
            'type' => 'password',
            'name' => 'password',
            'label' => 'Password',
            'validation' => 'required|min:8|confirmed'
        ],
        [
            'type' => 'password',
            'name' => 'password_confirmation',
            'label' => 'Confirm Password'
        ],
        [
            'type' => 'select',
            'name' => 'country',
            'label' => 'Country',
            'options' => [
                'id' => 'Indonesia',
                'ph' => 'Philippines',
                'my' => 'Malaysia'
            ],
            'validation' => 'required|in:id,ph,my'
        ],
        [
            'type' => 'checkbox',
            'name' => 'agree_terms',
            'label' => 'I agree to the terms',
            'checkbox_label' => 'I have read and agree to the Terms of Service',
            'validation' => 'required'
        ]
    ],
    'buttons' => [
        [
            'type' => 'submit',
            'label' => 'Create Account'
        ]
    ]
];

// In your controller
$form = FormBuilder::fromArray($userFormConfig);

// In your blade template
{{ $form->render() }}

// Extract validation rules
$rules = $form->getValidationRules();

Bootstrap Version Differences

FormBuilder automatically handles differences between Bootstrap 4 and Bootstrap 5:

Select Fields

// Bootstrap 4: Uses custom-select class
// Bootstrap 5: Uses form-select class (custom-select removed)

Form Groups

// Bootstrap 4: form-group class
// Bootstrap 5: mb-3 utility class

Form Labels

// Bootstrap 4: form-control-label class
// Bootstrap 5: form-label class

Form Rows

// Bootstrap 4: form-row for horizontal layouts
// Bootstrap 5: row with g-3 gap utility

CSS & JS Requirements

// Bootstrap 4: Requires jQuery, Popper.js, Bootstrap JS
// Bootstrap 5: Bootstrap bundle includes Popper.js (no jQuery needed)

The framework automatically includes the correct CDN links and styling classes!

Creating Custom Field Types

Implement the FieldType contract:

namespace App\FormBuilder\FieldTypes;

use BagasTopati\FormBuilder\Contracts\FieldType;

class DateRangeFieldType implements FieldType
{
    public function getName(): string
    {
        return 'date_range';
    }

    public function render(array $config): string
    {
        // Your custom rendering logic
        return '<input type="text" class="daterange">';
    }

    public function getValidationRules(array $config): array
    {
        return [$config['name'] => 'required|date_format:Y-m-d'];
    }
}

Register in config:

// config/form-builder.php
'custom_field_types' => [
    'date_range' => App\FormBuilder\FieldTypes\DateRangeFieldType::class,
]

Methods Reference

FormBuilder

Static Methods

  • fromArray(array $config): static - Create form from array configuration
  • fromJson(string $json): static - Create form from JSON string

Instance Methods

  • toArray(): array - Export form configuration to array
  • toJson(int $options = 0): string - Export form configuration to JSON
  • getValidationRules(): array - Get all validation rules
  • getValidationMessages(): array - Get validation messages

Field Methods

  • text(string $name, ?string $label, array $options): static
  • email(string $name, ?string $label, array $options): static
  • password(string $name, ?string $label, array $options): static
  • number(string $name, ?string $label, array $options): static
  • textarea(string $name, ?string $label, array $options): static
  • select(string $name, array $options_list, ?string $label, array $options): static
  • checkbox(string $name, ?string $label, array $options): static
  • radio(string $name, array $options, ?string $label, array $options): static
  • file(string $name, ?string $label, array $options): static
  • group(string $label, array $fields): static
  • row(array $fields): static

Form Methods

  • post(): static - Set form method to POST
  • get(): static - Set form method to GET
  • put(): static - Set form method to PUT
  • delete(): static - Set form method to DELETE
  • patch(): static - Set form method to PATCH
  • multipart(): static - Enable multipart/form-data
  • attr(string $key, string|int|bool|null $value): static - Add form attribute
  • class(string|array $class): static - Add CSS class
  • render(): string - Render form to HTML

Testing

Run tests with:

php artisan test

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Configuration-Based Forms (Cara 2 & 3)

Problem

Form definitions and validation rules are often separated in Laravel controllers:

public function create()  // Display form
{
    $form = FormBuilder::fromArray($config);
    return view('form', compact('form'));
}

public function store(Request $request)  // Handle submission
{
    $validated = $request->validate($rules);
    // Form and validation are disconnected!
}

Solution: Store Forms in Config

Define your forms in config/form-builder.php and access them from both methods.

Step 1: Define Form in Config

// config/form-builder.php
'forms' => [
    'user_registration' => [
        'action' => '/users/store',
        'method' => 'POST',
        'fields' => [
            [
                'type' => 'text',
                'name' => 'name',
                'label' => 'Full Name',
                'validation' => 'required|string|max:255'
            ],
            [
                'type' => 'email',
                'name' => 'email',
                'label' => 'Email',
                'validation' => 'required|email|unique:users'
            ],
            [
                'type' => 'password',
                'name' => 'password',
                'label' => 'Password',
                'validation' => 'required|min:8|confirmed'
            ],
            [
                'type' => 'password',
                'name' => 'password_confirmation',
                'label' => 'Confirm Password'
            ]
        ],
        'buttons' => [
            ['type' => 'submit', 'label' => 'Register']
        ]
    ]
]

Step 2: Use Trait in Controller (Recommended)

use BagasTopati\FormBuilder\Traits\HasFormConfiguration;

class UserController extends Controller
{
    use HasFormConfiguration;

    public function create()
    {
        $form = $this->getFormBuilder('user_registration');
        return view('users.create', compact('form'));
    }

    public function store(Request $request)
    {
        $validated = $this->validateForm('user_registration', $request);
        User::create($validated);
        return redirect()->route('users.show', $validated);
    }
}

Available Trait Methods

$this->getFormBuilder($formName)              // Get FormBuilder instance
$this->validateForm($formName, $request)      // Validate request with form rules
$this->getFormValidationRules($formName)      // Get validation rules only
$this->getFormValidationMessages($formName)   // Get validation messages
$this->hasForm($formName)                     // Check if form exists
$this->getAllFormNames()                      // List all registered forms
$this->getFormConfiguration($formName)        // Get raw config array

Alternative: Without Trait

public function create()
{
    $config = config('form-builder.forms.user_registration');
    $form = FormBuilder::fromArray($config);
    return view('users.create', compact('form'));
}

public function store(Request $request)
{
    $config = config('form-builder.forms.user_registration');
    $form = FormBuilder::fromArray($config);
    $rules = $form->getValidationRules();
    $validated = $request->validate($rules);
}

Benefits

✅ Form and validation in one place
✅ No duplication between methods
✅ Changes to form automatically update validation
DRY principle - less code
Cleaner, more readable controller code

Validation Integration

3-Layer Validation

FormBuilder supports three levels of validation:

  1. HTML5 Validation (Browser) - Real-time user feedback
  2. Server-side (Laravel Validator) - Secure backend validation
  3. Custom Logic (Optional) - Business-specific validation

Extracting Validation Rules

$form = FormBuilder::fromArray($config);
$rules = $form->getValidationRules();

// Result: ['name' => 'required|string|max:255', 'email' => 'required|email']

// Use with Laravel validator
$validated = $request->validate($rules);

// Or with custom messages
$messages = [
    'name.required' => 'Please enter your name',
    'email.email' => 'Please enter a valid email'
];
$validated = $request->validate($rules, $messages);

Supported Validation Rules

  • required - Field is required
  • nullable - Field is optional
  • string - Must be string
  • email - Must be valid email
  • unique:table[,column] - Must be unique in database
  • min:5 - Minimum length (5 chars)
  • max:255 - Maximum length (255 chars)
  • confirmed - Must match field_confirmation
  • regex:/pattern/ - Custom regex pattern
  • in:value1,value2 - Must be one of values
  • before:date, after:date - Date validation
  • image, file, mimes:jpg,png - File validation
  • ... and all standard Laravel validation rules

HTML5 Attributes

Validation rules are automatically converted to HTML5 attributes:

'validation' => 'required|email|max:255'

// Generates:
// <input type="email" required maxlength="255">

Security

XSS Prevention

All output is automatically HTML-escaped to prevent XSS attacks:

$form = FormBuilder::fromArray($config);
echo $form->render(); // Safe - output is escaped

CSRF Protection

Always use Laravel CSRF token in your forms:

<form method="POST" action="/submit">
    @csrf
    {!! $form->render() !!}
</form>

SQL Injection Prevention

Always validate and use parameterized queries:

$rules = $form->getValidationRules();
$validated = $request->validate($rules);

// Safe - use validated data with Eloquent
User::create($validated);

Best Practices

  1. Always validate on backend - Never trust client-side only
  2. Use HTTPS - Ensure forms submitted over secure connection
  3. Keep dependencies updated - Update Laravel and dependencies regularly
  4. Use environment variables - Never hardcode sensitive data
  5. Sanitize output - FormBuilder escapes output automatically

Support

For issues, questions, or suggestions, please open an issue on GitHub.

Data Binding (Form Population)

Overview

FormBuilder provides powerful data binding support to pre-populate forms from models, objects, or arrays.

Fluent API Value() Method

Set individual field values using method chaining:

$form = FormBuilder::fromArray($config);

$form->text('name')->value('John');
$form->email('email')->value('john@example.com');
$form->select('role')->selected('admin');
$form->checkbox('agree')->checked(true);

Bind from Object/Model

Automatically populate form fields from an object or Eloquent model:

// From Eloquent model
$user = User::find(1);
$form = FormBuilder::fromArray($config)->bind($user);

// From stdClass or regular object
$data = (object)['name' => 'John', 'email' => 'john@example.com'];
$form = FormBuilder::fromArray($config)->bind($data);

Bind from Array

Populate form fields from an associative array:

$data = [
    'name' => 'John',
    'email' => 'john@example.com',
    'role' => 'admin'
];

$form = FormBuilder::fromArray($config)->bind($data);

Nested Property Access

FormBuilder supports nested property notation using dot notation:

// Nested objects
$form->bind($user); // Automatically binds user.profile.avatar if field name is 'user.profile.avatar'

// Nested arrays
$form->bind([
    'user' => [
        'profile' => [
            'avatar' => 'avatar.jpg'
        ]
    ]
]);

// In field configuration
'fields' => [
    ['type' => 'text', 'name' => 'user.profile.name']  // Will access $user->profile->name
]

Eloquent Relationships

FormBuilder automatically handles Eloquent relationships:

$post = Post::find(1);
$form = FormBuilder::fromArray([
    'action' => '/posts/store',
    'fields' => [
        ['type' => 'text', 'name' => 'title'],
        ['type' => 'text', 'name' => 'author.name']  // Accesses $post->author->name
    ]
])->bind($post);

Automatic Date Formatting

DateTime objects are automatically formatted:

$config = ['form-builder.binding.date_format' => 'Y-m-d'];  // In config/form-builder.php

$user = (object)['created_at' => new DateTime('2026-05-29')];
$form = FormBuilder::fromArray($config)->bind($user);
// Renders as: value="2026-05-29"

JavaScript Pre-Population

Export form data as JavaScript for client-side form population:

$form = FormBuilder::fromArray($config)->bind($data);

// In Blade view
<script>
    {!! $form->toJavaScriptVariables('formData') !!}
    
    // formData now contains the bound values
    console.log(formData);
</script>

Or use the default variable name:

<script>
    {!! $form->toJavaScriptObject() !!}
    // Outputs: const formData = {...}
</script>

Real-World Example: Edit Form

// Controller
class ProductController extends Controller
{
    use HasFormConfiguration;

    public function edit(Product $product)
    {
        // Get form and bind product data
        $form = $this->getFormBuilderWithData('product_edit', $product);
        return view('products.edit', compact('form', 'product'));
    }

    public function update(Request $request, Product $product)
    {
        $validated = $this->validateForm('product_edit', $request);
        $product->update($validated);
        return redirect()->route('products.show', $product);
    }
}

// In view
@extends('layouts.app')

@section('content')
    <h1>Edit Product</h1>
    {!! $form->render() !!}
@endsection

Configuration

Configure binding behavior in config/form-builder.php:

'binding' => [
    'auto_escape' => true,           // HTML escape bound values
    'date_format' => 'Y-m-d',        // Date format for DateTime objects
    'format_currency' => false,      // Automatic currency formatting
],

Security Issues

Found a security vulnerability? Please email bagas.topati@gmail.com with:

  • Description of the vulnerability
  • Steps to reproduce
  • Potential impact
  • Suggested fix