Creating custom props
This guide explains how to create custom prop types for the Livewire Page Builder.
Structure
To create a custom prop, you need to:
- Create a new prop class that extends the base
Prop
class - Create a corresponding Blade view for the prop
- Implement the required methods
Required Properties
type
- The namespace of your custom prop classlabel
- The label of the propdefault
- The default value of the prop
Creating a Custom Prop Class
Here's the basic structure for a custom prop class:
<?php
namespace App\Props;
use LivewirePageBuilder\Props\Prop;
class CustomProp extends Prop
{
public function render()
{
return view('props.custom-prop', [
'key' => $this->key,
'label' => $this->config['label'] ?? '',
'default' => $this->config['default'] ?? '',
]);
}
public function handleSubmit($value)
{
return $value;
}
}
Methods
-
render()
- Returns the Blade view for the prop
- Must pass the prop's key, label, and default value to the view
-
handleSubmit($value) (optional)
- Processes the submitted value
- Can be used to transform or validate the input
- Returns the processed value
-
hasInternalValidation() (optional)
- Returns a boolean indicating whether the prop handles its own validation
- When returning
true
, the prop's rules will be skipped and validation should be handled inhandleSubmit()
- Example of internal validation in
handleSubmit()
:
public function handleSubmit($value)
{
$validator = Validator::make(['file' => $value], ['file' => $this->config['rules']]);
if ($validator->fails()) {
throw new \Exception($validator->errors()->first());
}
return $value;
}- Any exceptions thrown during validation will be automatically added as errors to the page builder input field.
Creating the Blade View
Create a Blade view for your prop in resources/views/props/custom-prop.blade.php
:
<div class="flex flex-col space-y-1">
<x-lpb::inputs.label :for="$key">{{ $label }}</x-lpb::inputs.label>
<!-- Your custom input here -->
</div>
Important: Wire Model Binding
All custom prop inputs must use the wire model binding editingComponentProps.{{ $key }}
to properly update the component's properties. This is required for the page builder to track and save changes to your prop.
Example:
<input
type="text"
wire:model="editingComponentProps.{{ $key }}"
class="w-full rounded border border-gray-300"
value="{{ $default }}"
/>
Available Input Components
You can use these built-in input components in your Blade views:
- Text Input
<x-lpb::inputs.text wire:model="editingComponentProps.{{ $key }}" />
- Textarea
<x-lpb::inputs.textarea wire:model="editingComponentProps.{{ $key }}" />
- Number Input
<x-lpb::inputs.number
wire:model="editingComponentProps.{{ $key }}"
:min="$min"
:max="$max"
:step="$step"
/>
- Select
<x-lpb::inputs.select
:options="[
'value1' => 'Label 1',
'value2' => 'Label 2',
'value3' => 'Label 3'
]"
wire:model="editingComponentProps.{{ $key }}"
/>
- File Upload
<x-lpb::inputs.file
wire:model="editingComponentProps.{{ $key }}"
:accepts="$accepts"
:multiple="$multiple"
/>
- Label
<x-lpb::inputs.label :for="$key">{{ $label }}</x-lpb::inputs.label>
- Color
<x-lpb::inputs.color wire:model="editingComponentProps.{{ $key }}" />
Example: Custom Date Picker Prop
Here's a complete example of a custom date picker prop:
<?php
namespace App\Props;
use LivewirePageBuilder\Props\Prop;
class DatePicker extends Prop
{
public function render()
{
return view('props.date-picker', [
'key' => $this->key,
'label' => $this->config['label'] ?? '',
'default' => $this->config['default'] ?? now()->format('Y-m-d'),
'min' => $this->config['min'] ?? null,
'max' => $this->config['max'] ?? null,
]);
}
public function handleSubmit($value)
{
if (empty($value)) {
return null;
}
try {
return \Carbon\Carbon::parse($value)->format('Y-m-d');
} catch (\Exception $e) {
return $this->config['default'] ?? now()->format('Y-m-d');
}
}
}
Blade view (resources/views/props/date-picker.blade.php
):
<div class="flex flex-col space-y-1">
<x-lpb::inputs.label>{{ $label }}</x-lpb::inputs.label>
<input
type="date"
wire:model="editingComponentProps.{{ $key }}"
class="w-full rounded border border-gray-300"
value="{{ $default }}"
@if($min) min="{{ $min }}" @endif
@if($max) max="{{ $max }}" @endif
/>
</div>
Usage in component:
public static function getConfig(): array
{
return [
'name' => __('Custom Component'),
'description' => __('A custom component with a date picker.'),
'icon' => '/path/to/icon.svg',
'allow_children' => false,
'props' => [
'publishDate' => [
'type' => \App\Props\DatePicker::class,
'label' => __('Publish Date'),
'default' => now()->format('Y-m-d'),
'min' => now()->format('Y-m-d'),
'max' => now()->addDays(30)->format('Y-m-d'),
'rules' => ['nullable', 'date'],
],
]
];
}
Example: Custom File Upload Prop with Internal Validation
Here's an example of a custom file upload prop that uses internal validation:
<?php
namespace App\Props;
use LivewirePageBuilder\Props\Prop;
use Illuminate\Support\Facades\Validator;
class FileUpload extends Prop
{
public static function hasInternalValidation(): bool
{
return true;
}
public function render()
{
return view('props.file-upload', [
'key' => $this->key,
'label' => $this->config['label'] ?? '',
'default' => $this->config['default'] ?? '',
'accepts' => $this->config['accepts'] ?? '*',
'multiple' => $this->config['multiple'] ?? false,
]);
}
public function handleSubmit($value)
{
if (empty($value)) {
return null;
}
// Validate the file using internal validation
$validator = Validator::make(
['file' => $value],
['file' => $this->config['rules'] ?? ['required', 'file', 'max:10240']]
);
if ($validator->fails()) {
throw new \Exception($validator->errors()->first());
}
return $value;
}
}
Blade view (resources/views/props/file-upload.blade.php
):
<div class="flex flex-col space-y-1">
<x-lpb::inputs.label>{{ $label }}</x-lpb::inputs.label>
<x-lpb::inputs.file
wire:model="editingComponentProps.{{ $key }}"
:accepts="$accepts"
:multiple="$multiple"
/>
@error('editingComponentProps.' . $key)
<x-lpb::inputs.error :message="$message" />
@enderror
</div>
Usage in component:
public static function getConfig(): array
{
return [
'name' => __('File Upload Component'),
'description' => __('A component with custom file upload validation.'),
'icon' => '/path/to/icon.svg',
'allow_children' => false,
'props' => [
'document' => [
'type' => \App\Props\FileUpload::class,
'label' => __('Upload Document'),
'accepts' => '.pdf,.doc,.docx',
'multiple' => false,
'rules' => ['required', 'file', 'max:10240', 'mimes:pdf,doc,docx'],
],
]
];
}
In this example:
- The
hasInternalValidation()
method returnstrue
, indicating that this prop handles its own validation - The
handleSubmit()
method performs custom validation using Laravel's Validator - If validation fails, it throws an exception with the first error message
- The error will be automatically displayed in the page builder's input field
- The validation rules are still defined in the component config but are used internally by the prop