.png)
By Ferry
Oct 21, 2025
11 views
Build a Custom Color Input Type in CRUDBooster
Sometimes you need a field that feels tailor‑made — like a color picker for brand guidelines, themes, or status indicators. CRUDBooster supports custom input types with a clean, predictable structure. In this article, we’ll build `custom-color` that’s consistent, maintainable, and production‑ready.
The goal isn’t just “it works”, but “it’s worth using”: consistent code, easy maintenance, and aligned with CRUDBooster patterns.
Prerequisites
- PHP `8.2+`
- Laravel `11.x` or `12.x`
- CRUDBooster installed and running
- Familiarity with Livewire, Blade, and Service Providers
Why this matters:
- PHP/Laravel versions affect syntax, lifecycle, and package compatibility.
- CRUDBooster must be active so the registrar and theme can discover your type.
- Livewire and Blade are the foundation for rendering and data binding in CRUDBooster forms.
Practical tip:
- Store custom types under `app/Cb/Types`. It cleanly separates types from modules, makes discovery easier, and keeps a tidy architecture.
What We’ll Build
We’ll create a color input type `custom-color` that provides:
- A form template rendering a native, lightweight color picker.
- A view template for detail/read‑only pages with a small swatch.
- An option class to manage defaults and small reusable behaviors.
- A service provider to register the type with an explicit alias.
Why `custom-color`?
- Native `<input type="color">` is lightweight and sufficient for many needs.
- Easy to upgrade to a richer picker (palettes, alpha) via JS/CSS assets.
- Ideal for UI/theme configuration, plain text storage, and fast rendering in views.
Folder & File Structure
This structure ensures CRUDBooster can discover and render the type.
app/Cb/Types/Color
|
├── Function
│ └── Color.php
├── views
│ ├── form.blade.php
│ └── view.blade.php
└── ColorServiceProvider.php
File roles:
- `Function/Color.php`: option class for reusable settings (e.g., default color, preview toggle).
- `views/form.blade.php`: the form template (with Livewire binding and minimal a11y).
- `views/view.blade.php`: the read‑only/detail template so users see a swatch and the value.
- `ColorServiceProvider.php`: registers the view alias and the type with the CRUDBooster registrar.
Best practices:
- Use a clear, consistent alias (`custom-color`) so it’s easy to reference.
- Keep options in `Function/Color.php` so Form builder usage stays clean.
Register the Type (Service Provider)
The provider ties everything together: it loads views, registers the type, and (optionally) adds assets.
<?php
// File: app/Cb/Types/Color/ColorServiceProvider.php
namespace App\Cb\Types\Color;
use Illuminate\Support\ServiceProvider;
use CrudBooster\Components\Type\CBTypeRegistrar;
class ColorServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Register Blade views under the alias `custom-color`
$this->loadViewsFrom(__DIR__.'/views', 'custom-color');
// Register the type with CRUDBooster (color fits the text group)
CBTypeRegistrar::addText([
'type' => 'custom-color', // type name
'form' => 'custom-color::form', // form view alias
'view' => 'custom-color::view', // read-only view alias
'clazz' => Function\Color::class, // option class
'generalOption' => true, // enable built-in general options
]);
}
}
Then register the provider so Laravel includes it:
<?php
// File: app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Cb\Types\Color\ColorServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->register(ColorServiceProvider::class);
}
}
Key notes:
- `loadViewsFrom` exposes a namespace alias used by the registrar in `form` and `view`.
- `CBTypeRegistrar::addText` places the type in the correct group (text‑like), which matters for UI integration and default behavior.
- `generalOption => true` unlocks CRUDBooster’s built‑in input transformations from the module builder.
Type Groups You Can Use
Choose the right registrar to match UI/behavior expectations:
- `addText` — text‑like inputs (we use this for color)
- `addDateTime` — date & time inputs
- `addWysiwyg` — rich text editor
- `addNumeric` — numeric (number, money, decimal) with formatting
- `addUpload` — file/image upload
- `addPassword` — password fields with safe behavior
- `addJson` — structured JSON input
- `addSelect` — choices (select, radio, checkbox)
- `addMap` — maps and coordinates
How to choose:
- Consider how the value is stored (string vs numeric vs JSON) and how built‑in UI behaves.
- Pick the group that best matches the base behavior of your type so defaults stay relevant.
General Options (Built‑ins)
With `'generalOption' => true`, you can use helpful transformations from the module builder:
- `uppercase`, `lowercase`, `noSpace`, `noSpecialChar`
- `numeric`, `nonNumeric`, `numberFormat`, `phoneFormat`
Context for color:
- Colors are typically hex strings (`#RRGGBB`). Avoid `numeric/nonNumeric` for this use case.
- `noSpace` or `noSpecialChar` is irrelevant with native `<input type="color">`, but useful if you fall back to a manual text input.
Form Template (Blade)
The form template defines how the input is rendered and bound to Livewire. Keep accessibility (labels, focus) and responsiveness in mind.
{{-- File: app/Cb/Types/Color/views/form.blade.php --}}
{{-- The $column variable contains key, placeholder, label, helpText, etc. --}}
<input type="color"
id="{{$column['key']}}"
{{ $focus ? 'autofocus' : '' }}
placeholder="{{$column['placeholder'] ?? ''}}"
@readonly($column['readonly'] ?? false)
wire:loading.attr="readonly"
wire:target="formSave"
@if(isset($column['live']))
wire:model.live.debounce.{{$column['live']}}ms="formData.{{$column['key']}}"
@else
wire:model="formData.{{$column['key']}}"
@endif
class="form-control">
Binding details:
- `wire:model` binds the value to `formData[key]` so CRUDBooster manages form state.
- `wire:loading.attr="readonly"` prevents changes while submitting.
- `autofocus` improves UX when the field is important.
Optional extras:
- Render the label and helpText from `$column` outside the input to improve accessibility.
- Use `debounce` if color changes trigger heavy preview updates.
View Template (Blade)
The view template shows the saved value on detail/read‑only pages. A small swatch helps with quick visual verification.
{{-- File: app/Cb/Types/Color/views/view.blade.php --}}
{{-- Available: $column, $value, $formData --}}
@php
$color = $value ?? ($column['option']['default'] ?? '#000000');
@endphp
<span style="display:inline-block;width:16px;height:16px;background-color:{{ $color }};border:1px solid #ddd;border-radius:3px;margin-right:8px;"></span>
<code>{{ $color }}</code>
Display tips:
- Keep the swatch small so it doesn’t disrupt layout.
- Show the color code for easy copying.
- Provide a fallback like `#000000` when the value is missing.
Type Option Class
Options make configuration reusable and clean when using the Form builder.
<?php
// File: app/Cb/Types/Color/Function/Color.php
namespace App\Cb\Types\Color\Function;
use CrudBooster\Components\Type\TypeOptionAbstract;
class Color extends TypeOptionAbstract
{
/**
* Set a default color (hex), e.g., #FF6B6B
*/
public function default(string $hex): static
{
$this->option['default'] = $hex;
return $this;
}
/**
* Show or hide the preview swatch in the form
*/
public function showPreview(bool $show): static
{
$this->option['showPreview'] = $show;
return $this;
}
}
Option best practices:
- Validate hex values at the form layer if needed (e.g., Laravel rules) to keep data safe.
- Keep options chainable: `Color::option()->default('#FF6B6B')->showPreview(true)`.
Adding CSS & JS Assets (Optional)
Need richer features (palettes, sampling, alpha)? Register CSS/JS assets — they’re automatically injected into the CRUDBooster theme header.
<?php
// Inside: app/Cb/Types/Color/ColorServiceProvider.php
use CrudBooster\Themes\CbThemeAssetRegistrar;
CbThemeAssetRegistrar::addCss('https://example.com/color-picker.css');
CbThemeAssetRegistrar::addJs('https://example.com/color-picker.js');
Notes:
- Ensure assets aren’t too heavy to keep the admin panel snappy.
- Consider self‑hosting assets if CDN access is unreliable in your environment.
Use It in a Form
Here’s a realistic usage in a Form Component — perfect for theme or branding settings.
<?php
use CrudBooster\Livewire\FormBuilder\Form;
use App\Cb\Types\Color\Function\Color;
class ThemeForm extends \CrudBooster\Livewire\BaseFormComponent
{
public function init(): void
{
$this->makeForm([
Form::add(label: 'Brand Color', key: 'brand_color', type: 'custom-color')
->option(Color::option()->default('#FF6B6B')->showPreview(true)),
]);
}
}
Database suggestions:
- Store as `VARCHAR(7)` for hex `#RRGGBB`, or `VARCHAR(9)` if you support alpha `#RRGGBBAA`.
- Use a migration default if you want an initial value when records are created.
Workflow at a Glance
- Create the folder structure and files.
- Write lightweight, accessible form and view templates.
- Add an option class under `Function` for reusable configuration.
- Register the service provider and call `CBTypeRegistrar` for the right group.
- (Optional) Add CSS/JS assets for advanced pickers.
- Use `Form::add` with your type name and chain options.
- Test in a real module and watch UX and data consistency.
Tips That Save Time
- Keep `type` names, view aliases, and class names consistent.
- Use a short, clear alias with `loadViewsFrom` (e.g., `custom-color`).
- Enable `generalOption` only if it’s relevant to your input behavior.
- Keep types under `app/Cb/Types` for a clean separation from modules.
Troubleshooting
- Type not recognized: ensure the service provider is registered and `CBTypeRegistrar` is called.
- View not found: check your `loadViewsFrom` alias matches the registrar config.
- Options not applied: make sure your option class extends `TypeOptionAbstract` and the template uses `$column['option']`.
- Livewire not updating: verify `wire:model` targets `formData[key]` and there are no binding errors.
- CSS/JS not loading: verify URLs/access and ensure `CbThemeAssetRegistrar::addCss/addJs` is called.
Loading...
Just now