juniora/nova-color-field
A color picker field for Laravel Nova that lets you add hex color selection to your resources, forms, and filters. Simple to install via Composer and ready to drop into any Nova resource.
This is a single-purpose Nova field package: it ships one field class plus a compiled Vue component. Treat it as a snippet library rather than infrastructure — the whole package is probably ~5 files, so read it before you rely on it.
Install and verify compatibility first.
composer require juniora/nova-color-field
Before writing any code, open vendor/juniora/nova-color-field/composer.json and check the laravel/nova constraint. The last release is 2024-12-21, which predates Nova 5. If you're on Nova 5, expect to fork or patch the constraint — the field API is stable enough that this usually works, but the Vue component's imports (laravel-nova, laravel-mix vs Vite) are the part that breaks.
Check that dist/ is committed. Nova resolves a field's frontend from the field.js path declared in the field class. If the repo omits the compiled bundle, you get a silently blank/missing component and must build it yourself:
cd vendor/juniora/nova-color-field
npm install && npm run prod # or dev
That path is gitignored inside vendor/, so it disappears on every composer install — a hard blocker for CI. In that case copy the field into your app with php artisan nova:field acme color and port the Vue component.
First use case — a brand/accent color on a settings resource:
use Juniora\NovaColorField\ColorField; // confirm the exact namespace in src/
ColorField::make('Brand Color', 'brand_color')
->rules('required', 'regex:/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/')
->help('Hex value, e.g. #4F46E5'),
with a matching column:
$table->string('brand_color', 7)->nullable();
Where to look first, in order: src/*.php (the field class — this tells you what withMeta keys the component actually reads), resources/js/components/Field.vue (what props/options exist), dist/js/field.js (does it exist), and composer.json.
Normalize on the way in and out. Pickers are inconsistent about what they emit — #fff vs #ffffff, with or without the hash, sometimes '' for cleared. Pin it down at the field level so the rest of your app can assume one format:
ColorField::make('Brand Color')
->resolveUsing(fn ($value) => $value ? strtolower($value) : null)
->fillUsing(function ($request, $model, $attribute) {
$value = $request->input($attribute);
$model->{$attribute} = $value ? strtolower(trim($value)) : null;
})
->nullable(),
Do not store the color as-is and normalize at render time; you'll end up with three formats in the column within a month.
Index views need a swatch, not a hex string. Most color fields render as plain text on the index, which is useless at a glance. Options, roughly in order of effort:
->hideFromIndex() if the color is a detail-page concern only.Text::make('Color', fn () => $this->brand_color)->asHtml() with an inline <span style="..."> — sanitize if the value is user-writable.index component via ->withMeta() if the package supports a swatch mode (check the Vue file).Palettes enforce brand consistency. If the component accepts preset swatches (usually a withMeta(['palette' => [...]]) or a dedicated method), use it. Free-form color input on a marketing site produces #FF0000 and #f00 and red and everyone hates it six months later.
Pair it with a semantic field, don't replace one. A color column is a presentation detail. If the color means something — status, priority, category — store an enum/status string and map to colors in the Nova layer or a config file:
Badge::make('Status')->map([
'active' => 'green',
'archived' => 'gray',
])
Storing raw hex for semantic state means a redesign becomes a data migration. Reserve ColorField for genuinely presentational choices: brand accents, chart series colors, label backgrounds on a per-record basis.
JSON columns for multi-color sets. If you need two or three coordinated colors (background + foreground + accent), store a JSON object and validate keys individually with ->rules(['array', 'accent.regex:...']). Don't invent column-per-color schemas.
Multi-tenant / settings resources are the sweet spot: a singleton Setting model or a BrandTheme resource where an admin picks colors, and the frontend reads them via a cached config. Wrap reads in Cache::remember and bust on saved.
Testing. Nova's own Nova::assertField / field assertion helpers cover existence and metadata but not interaction. For anything beyond presence, use Laravel Dusk against a real Nova page, or skip the component entirely in tests and assert the normalized value written by fillUsing — that's the part that actually breaks.
app/Nova/Fields/ via php artisan nova:field and own it. That's an hour of work and removes the package from your composer.json risk surface entirely.laravel-nova import paths). A package last released in Dec 2024 is a coin flip on Nova 5. Pin to a working Nova minor or fork; don't discover this in a production deploy.varchar(7) holds #ffffff. It does not hold 8-digit hex with alpha (#ffffffcc), rgb(...), or hsl(...). If the component supports an alpha slider or format switch, use varchar(9) or varchar(32) from day one — widening a column later is easy, but the dirty data written in the meantime is not.'' when cleared. Without a fillUsing/mutator, you get '' in a nullable column and whereNull('brand_color') starts lying to you. Normalize '' → null explicitly.style attribute on the public site, validate against a strict hex regex server-side on write — never trust the picker's output. A value like red;background:url(...) is not a color.$component property matches the registered Vue component name, confirm dist/js/field.js is present and readable by the web server (ls -l, check permissions after composer install as a different user), then hard-refresh — Nova's field assets are cached aggressively by the browser.->withMeta([...]), ->resolveUsing(), ->fillUsing(), ->displayUsing(), and ->component('your-own') let you reskin it against your own Vue component while keeping the PHP-side ergonomics. If the bundled component disappoints, keep the field class and swap the component rather than abandoning the package.WHERE col LIKE ?, which is meaningless for colors; adding a color column to $search is noise. Sortable color columns are equally pointless — resist.How can I help you explore Laravel packages today?