Skip to content

Fluent API Customization

All UI, theme, type registration, and slot customization is done via the fluent PHP API — not in config/scoutify.php. Register calls in AppServiceProvider::boot() (or any service provider).

use Matheusmarnt\Scoutify\Facades\Scoutify;
use Matheusmarnt\Scoutify\Support\UiConfig;
public function boot(): void
{
Scoutify::types()-> /* ... */ ;
Scoutify::theme()-> /* ... */ ;
Scoutify::configureUi(function (UiConfig $ui) { /* ... */ });
}

Returns the singleton TypesConfig instance. All calls are additive and resolved on first use.

Register an Eloquent model as a searchable type:

Scoutify::types()->register(
\App\Models\User::class,
label: 'Users',
icon: 'user', // short name — prefix applied automatically
color: 'indigo', // Tailwind color name or custom token
);
ParameterTypeDefaultDescription
$classclass-stringFully-qualified model class
labelstring''Display name in the filter chip and group header
iconstring''Icon identifier (short name or full Blade Icons name)
colorstring'zinc'Accent color — any Tailwind color name or a custom token registered via theme()->color()

Chain multiple calls to register several models at once:

Scoutify::types()
->register(\App\Models\User::class, label: 'Users', icon: 'user', color: 'indigo')
->register(\App\Models\Post::class, label: 'Posts', icon: 'document-text', color: 'sky')
->register(\App\Models\Product::class, label: 'Products', icon: 'shopping-bag', color: 'emerald');

Set a global prefix prepended to short icon names before resolving via Blade Icons:

Scoutify::types()->iconPrefix('heroicon-o-');
// 'user' → 'heroicon-o-user'

If a model returns a full icon name (e.g. ri-user-line) that does not start with the prefix, it is used as-is.


Returns the singleton ThemeConfig instance. Override any Tailwind class strings applied to modal elements. Unset methods fall back to the package defaults.

MethodElementDefault classes
dialogPanel(string)Outer positioning wrapper (no background)relative w-full md:max-w-2xl
dialogContent(string)Inner content card (background, shadow, rounded corners)flex max-h-[90dvh] min-h-0 flex-col overflow-hidden rounded-t-2xl bg-white pb-[env(safe-area-inset-bottom)] md:max-h-[80vh] md:rounded-xl md:shadow-2xl md:ring-1 md:ring-zinc-900/5 dark:bg-zinc-900 dark:md:ring-white/10
dialogScrim(string)Backdrop overlayabsolute inset-0 bg-zinc-950/50
input(string)Search input fieldblock w-full rounded-lg border border-zinc-200 bg-white py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none transition focus-visible:border-zinc-300 focus-visible:ring-2 focus-visible:ring-scoutify-accent/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder-zinc-500 [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden
trigger(string)Desktop trigger button (<x-scoutify::gs.trigger />)group inline-flex h-9 min-w-16 items-center gap-2 rounded-lg border border-zinc-200 bg-white px-3 text-sm text-zinc-500 transition hover:border-zinc-300 hover:text-zinc-700 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-scoutify-accent/40 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:text-zinc-200
triggerMobile(string)Mobile trigger button (<x-scoutify::gs.trigger-mobile />)lg:hidden inline-flex size-11 items-center justify-center rounded-lg border border-zinc-200 bg-white text-zinc-500 transition hover:border-zinc-300 hover:text-zinc-700 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-scoutify-accent/40 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:text-zinc-200
toggleActive(string)Active state of filter toggle switchbg-indigo-600 dark:bg-indigo-500
toggleInactive(string)Inactive state of filter toggle switchbg-zinc-200 dark:bg-zinc-700
accent(string)Modal wrapper — injects --scoutify-accent CSS custom propertyNo default override — package CSS defines the scoutify-accent token
previewButton(string)Eye button on result rows (opens preview pane) — appendedinline-flex size-7 shrink-0 items-center justify-center rounded-md text-zinc-400 transition hover:bg-zinc-100 hover:text-zinc-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 dark:hover:bg-zinc-800 dark:hover:text-zinc-300
downloadButton(string)Download button on result rows and preview header — appendedinline-flex size-7 shrink-0 items-center justify-center rounded-md text-zinc-400 transition hover:bg-zinc-100 hover:text-zinc-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 dark:hover:bg-zinc-800 dark:hover:text-zinc-300 (result rows); preview header uses size-8 variant
previewBackButton(string)Back arrow button in preview pane header — appendedinline-flex size-8 shrink-0 items-center justify-center rounded-lg text-zinc-500 transition hover:bg-zinc-100 hover:text-zinc-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 dark:hover:bg-zinc-800 dark:hover:text-zinc-200

Copy the default and change only the bg-* classes:

Scoutify::theme()
->dialogContent(
'flex max-h-[90dvh] min-h-0 flex-col overflow-hidden rounded-t-2xl ' .
'bg-slate-800 ' . // ← your custom background
'pb-[env(safe-area-inset-bottom)] md:max-h-[80vh] md:rounded-xl ' .
'md:shadow-2xl md:ring-1 md:ring-zinc-900/5 dark:bg-slate-900 dark:md:ring-white/10'
);

dialogPanel controls max-width. Override only the sizing:

Scoutify::theme()
->dialogPanel('relative w-full md:max-w-4xl');

accent() accepts any CSS color value — a hex, rgb(), or CSS variable. It injects --scoutify-accent as an inline custom property on the modal wrapper, which cascades to all child elements. No Tailwind safelist needed:

Scoutify::theme()->accent('#7c3aed');
// or: ->accent('var(--color-violet-600)') // Tailwind CSS 4 token

All focus rings, input borders, and highlighted elements reference --scoutify-accent through the package’s scoutify-accent token.

previewButton(), downloadButton(), and previewBackButton() append classes to the element’s existing base — they do not replace them. Use them to add hover effects, backgrounds, or Tailwind overrides:

Scoutify::theme()
->previewButton('hover:bg-indigo-100 dark:hover:bg-indigo-900')
->downloadButton('hover:bg-emerald-100 dark:hover:bg-emerald-900')
->previewBackButton('hover:bg-zinc-200 dark:hover:bg-zinc-700');

Register named color tokens that models can reference via color: 'brand' in register():

Scoutify::theme()->color('brand', 'bg-violet-100 text-violet-700', 'dark:bg-violet-900/60 dark:text-violet-200');

Then use in registration:

Scoutify::types()->register(\App\Models\Order::class, label: 'Orders', icon: 'shopping-cart', color: 'brand');

Scoutify::configureUi() — Visibility Flags & Slots

Section titled “Scoutify::configureUi() — Visibility Flags & Slots”

Pass a closure that receives a fresh UiConfig instance each time the modal boots:

use Matheusmarnt\Scoutify\Support\UiConfig;
Scoutify::configureUi(function (UiConfig $ui) {
$ui->showTypeChips(false)
->showHintBar(false);
});
MethodDefaultDescription
showTypeChips(bool)trueModel-type filter chips above results
showToggleOnlyActive(bool)true”Active only” toggle
showToggleIncludeTrashed(bool)true”Include trashed” toggle
showHintBar(bool)trueKeyboard-shortcut hint bar at the bottom
showIdleHint(bool)truePrompt text paragraph in the idle state — does not hide the magnifying-glass icon or recent-list

Inject custom content into named positions inside the modal. Slots receive a SlotContext with the current search state:

Slot keyPositionBehavior
header-trailingAfter the search input in the modal headerAppended
idle-extraAfter the idle state (before the user types)Appended
after-resultsAfter the last result groupAppended
empty-stateWhen a non-blank query returns 0 resultsReplaces the default “No results” component; $ctx->query available
use Matheusmarnt\Scoutify\Support\SlotContext;
use Matheusmarnt\Scoutify\Support\UiConfig;
Scoutify::configureUi(function (UiConfig $ui) {
// Blade view name
$ui->slot('after-results', 'search.footer');
// Closure returning an HtmlString
$ui->slot('empty-state', function (SlotContext $ctx) {
return new \Illuminate\Support\HtmlString(
"<p class=\"text-sm text-gray-400\">No results for \"{$ctx->query}\".</p>"
);
});
// Blade component class-string
$ui->slot('empty-state', \App\View\Components\SearchEmptyState::class);
});
PropertyTypeDescription
$wireModalLivewire component instance
$querystringCurrent search query string
$resultsCollectionCurrent results as a Laravel Collection — supports ->count(), ->first(), ->filter(), etc.
$hasResultsboolWhether results are non-empty
$isIdleboolWhether the user has not typed yet

A slot value can be:

  • A Blade view name (string) — rendered with SlotContext::toArray() as view data
  • A Closure receiving SlotContext — return HtmlString, a View, or a castable-to-string value
  • A Blade Component class-string — constructor parameters auto-matched from SlotContext

<?php
namespace App\Providers;
use App\Models\Order;
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\HtmlString;
use Matheusmarnt\Scoutify\Facades\Scoutify;
use Matheusmarnt\Scoutify\Support\SlotContext;
use Matheusmarnt\Scoutify\Support\UiConfig;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Scoutify::types()
->iconPrefix('heroicon-o-')
->register(User::class, label: 'Users', icon: 'user', color: 'indigo')
->register(Post::class, label: 'Posts', icon: 'document-text', color: 'sky')
->register(Order::class, label: 'Orders', icon: 'shopping-cart', color: 'brand');
Scoutify::theme()
->dialogPanel('relative w-full md:max-w-4xl')
->color('brand', 'bg-violet-100 text-violet-700', 'dark:bg-violet-900/60 dark:text-violet-200');
Scoutify::configureUi(function (UiConfig $ui) {
$ui->showHintBar(false)
->slot('empty-state', function (SlotContext $ctx) {
return new HtmlString(
"<p class=\"text-center text-sm text-gray-400 py-6\">No results for &ldquo;{$ctx->query}&rdquo;.</p>"
);
});
});
}
}