PHPackages                             ifsware/filament-omnisearch - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Search &amp; Filtering](/categories/search)
4. /
5. ifsware/filament-omnisearch

ActiveLibrary[Search &amp; Filtering](/categories/search)

ifsware/filament-omnisearch
===========================

Powerful dynamic omnisearch plugin for Filament

v1.1.0(2w ago)43MITPHPPHP ^8.2

Since Jul 9Pushed 2w agoCompare

[ Source](https://github.com/Ifsware/filament-omnisearch)[ Packagist](https://packagist.org/packages/ifsware/filament-omnisearch)[ RSS](/packages/ifsware-filament-omnisearch/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)Dependencies (13)Versions (5)Used By (0)

Filament Omnisearch
===================

[](#filament-omnisearch)

A keyboard-first command palette for [Filament v5](https://filamentphp.com). Press `⌘K` / `Ctrl+K` from anywhere in your panel to instantly search navigation items, database records, resource actions, and custom commands — without leaving your current page.

Think of it as Spotlight or VS Code's command palette, built specifically for Filament. It replaces (or complements) Filament's native global search with a unified interface that covers everything: navigation, records, panel switching, page-specific actions, and any custom scope you register.

Preview
-------

[](#preview)

[![preview](resources/img/preview.png)](resources/img/preview.png)

Features
--------

[](#features)

- **Keyboard-driven** — Open with `⌘K` / `Ctrl+K`, navigate with arrow keys, execute with Enter, trigger actions with custom shortcuts
- **Built-in Scopes** — Navigation, Resources, Panels, Actions, and Page Actions out of the box
- **Extensible** — Add custom scopes by implementing `OmnisearchScope`
- **Record Preview** — Side panel showing resource field details when a result is active
- **Recent Searches** — Persisted per-browser with configurable toggle
- **Page Actions** — Auto-detect Create/List actions; discoverable from any page via search
- **Dark Mode** — Follows Filament's theme automatically
- **Fuzzy Search** — Smart matching with keyword support
- **Multilingual** — Built-in translations for English, Indonesian, and Dutch

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11+
- Filament v5
- Livewire v4

Installation
------------

[](#installation)

```
composer require ifsware/filament-omnisearch
```

Register the plugin in your Filament panel provider:

```
use Ifsware\Omnisearch\OmnisearchPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            OmnisearchPlugin::make(),
        ]);
}
```

Publish the config file:

```
php artisan vendor:publish --tag="omnisearch-config"
```

---

What Works Out of the Box
-------------------------

[](#what-works-out-of-the-box)

After installation and registration, opening the omnisearch palette (`⌘K` / `Ctrl+K`) gives you five built-in scopes automatically.

### 1. Navigation Scope

[](#1-navigation-scope)

Searches all visible navigation items registered in the current panel. No configuration needed — every item in your sidebar is immediately searchable.

### 2. Resource Scope

[](#2-resource-scope)

Searches **records** across all resources that have Filament's global search enabled. A resource is searchable only when it declares at least one of:

- `$recordTitleAttribute` — the column used as the result title
- `getGlobalSearchResultDetails()` — method override returning fields shown in the subtitle and preview panel
- A custom `getGlobalSearchResults()` implementation

**Minimal example** — make a resource record searchable:

```
class UserResource extends Resource
{
    protected static ?string $model = User::class;

    // Required: tells Filament which column is the record "name"
    protected static ?string $recordTitleAttribute = 'name';
}
```

**With detail fields shown in the preview panel:**

```
class UserResource extends Resource
{
    protected static ?string $model = User::class;

    protected static ?string $recordTitleAttribute = 'name';

    // These fields appear in the side preview when a result is highlighted
    public static function getGlobalSearchResultDetails(Model $record): array
    {
        return [
            'Email' => $record->email,
        ];
    }
}
```

> If `$recordTitleAttribute` is not set and `getGlobalSearchResultDetails()` is not implemented, the resource will not appear in omnisearch results.

### Scoping Resource Search Results

[](#scoping-resource-search-results)

`OmnisearchResourceScope` uses Filament's built-in global search under the hood. For each searchable resource it calls `getGlobalSearchEloquentQuery()`, which by default delegates to `getEloquentQuery()`.

**This means your existing `getEloquentQuery()` scope is inherited by omnisearch — but only if it is always applied.** If the scope is conditional (e.g. based on user role), omnisearch may bypass it when the condition is not met.

The safest approach is to override `getGlobalSearchEloquentQuery()` explicitly in your resource:

```
use Illuminate\Database\Eloquent\Builder;

class OrderResource extends Resource
{
    // Scopes the list table (and is also called by global search by default)
    public static function getEloquentQuery(): Builder
    {
        $query = parent::getEloquentQuery();

        if (auth()->user()?->isCustomer()) {
            $query->where('user_id', auth()->id());
        }

        return $query;
    }

    // Explicitly scope omnisearch results — do not rely on getEloquentQuery() alone
    public static function getGlobalSearchEloquentQuery(): Builder
    {
        $query = parent::getGlobalSearchEloquentQuery();

        if (auth()->user()?->isCustomer()) {
            $query->where('user_id', auth()->id());
        }

        return $query;
    }
}
```

With this setup:

- **Customers** — omnisearch only returns their own orders
- **Admins** — omnisearch returns all orders (no scope applied)

> **Multi-panel apps** — if the same model is exposed in multiple panels (e.g. an admin panel and a customer portal), each panel's `Resource` class is independent. Apply scoping only in the portal resource; leave the admin resource untouched.

### 3. Panel Scope

[](#3-panel-scope)

Lists other Filament panels the authenticated user has access to. Useful in multi-panel applications (e.g. admin + customer portal). Results appear even without a search query, so users can switch panels from the palette.

> **Panel visibility requires `FilamentUser`.**The panel scope checks access by calling `canAccessPanel()` on the authenticated user. This method is only available when your `User` model implements `Filament\Models\Contracts\FilamentUser`. Without it, the scope falls back to `app()->environment('local')` — meaning all panels are visible to all users in local development regardless of their role.
>
> To correctly restrict panel visibility, implement `FilamentUser` and define `canAccessPanel()`:
>
> ```
> use Filament\Models\Contracts\FilamentUser;
> use Filament\Panel;
>
> class User extends Authenticatable implements FilamentUser
> {
>     public function canAccessPanel(Panel $panel): bool
>     {
>         return match ($panel->getId()) {
>             'admin'  => $this->isAdmin(),
>             'portal' => $this->isAdmin() || $this->isCustomer(),
>             default  => false,
>         };
>     }
> }
> ```

### 4. Action Scope

[](#4-action-scope)

Shows global actions registered on the plugin (see [Global Actions](#global-actions) below). Actions appear without a search query so they are always discoverable.

### 5. Page Actions Scope

[](#5-page-actions-scope)

Automatically discovers **Create** and **List** actions for every resource whose pages use the `HasOmnisearchPageActions` trait. Unlike the per-page chip, these results appear in **search results from any page** — not only when the user is currently on that resource's page.

---

Global Actions
--------------

[](#global-actions)

Register application-level actions on the plugin using `OmnisearchAction`. Actions appear in the **Actions** group and can run any PHP callable.

```
use Ifsware\Omnisearch\OmnisearchPlugin;
use Ifsware\Omnisearch\OmnisearchAction;

OmnisearchPlugin::make()
    ->actions([
        OmnisearchAction::make('clear-cache')
            ->title('Clear Application Cache')
            ->subtitle('Run artisan cache:clear')
            ->icon('heroicon-o-trash')
            ->keywords(['cache', 'clear', 'flush'])
            ->shortcut('mod+shift+c')
            ->action(fn () => \Artisan::call('cache:clear')),

        OmnisearchAction::make('go-profile')
            ->title('My Profile')
            ->subtitle('Open your profile settings')
            ->icon('heroicon-o-user')
            ->keywords(['profile', 'account', 'settings'])
            ->shortcut('mod+shift+u')
            ->action(fn () => redirect()->to(Filament::getProfileUrl() ?? '/admin')),
    ]);
```

`OmnisearchAction` fluent API:

MethodDescription`title(string)`Display title in the palette`subtitle(string)`Secondary text below the title`icon(string)`Heroicon name, e.g. `heroicon-o-bolt``keywords(array)`Extra words used for fuzzy matching`shortcut(string)`Keyboard shortcut — shown as a badge in the result **and** registered as an active hotkey (see [Keyboard Shortcuts](#keyboard-shortcuts))`action(callable)`PHP callable invoked when the action is executed---

Page Actions
------------

[](#page-actions)

Add `HasOmnisearchPageActions` to a resource page to expose **Create** and **List** actions in two places:

1. **Topbar chip** — shown while the user is on that page, for quick one-click access
2. **Search results** — available from any page via `OmnisearchPageActionsScope` (see [built-in scopes](#5-page-actions-scope))

```
use Ifsware\Omnisearch\Concerns\HasOmnisearchPageActions;

class ListUsers extends ListRecords
{
    use HasOmnisearchPageActions;

    protected static string $resource = UserResource::class;
}
```

You only need to add the trait to **one page** per resource (typically the list page). The `OmnisearchPageActionsScope` scans all resource pages automatically, so Create/List actions will appear in search results regardless of which page the user is currently on.

When the page is active, omnisearch will show in the topbar:

- **Create User** — links to the resource create page (if it exists)
- **Users** — links back to the resource index (if it exists)

### Adding Custom Page Actions

[](#adding-custom-page-actions)

Override `getOmnisearchActions()` to inject page-specific actions alongside the auto-detected ones. A `shortcut` key registers an active keyboard hotkey — but only **while the user is on that page**:

```
class EditUser extends EditRecord
{
    use HasOmnisearchPageActions;

    protected static string $resource = UserResource::class;

    protected function getOmnisearchActions(): array
    {
        return [
            [
                'id'       => 'page.send-welcome',
                'type'     => 'action',
                'group'    => 'Page',
                'title'    => 'Send Welcome Email',
                'subtitle' => 'Dispatch a welcome email to this user',
                'icon'     => 'heroicon-o-envelope',
                'keywords' => ['email', 'welcome', 'send'],
                'shortcut' => 'mod+shift+e',    // active only on this page
            ],
        ];
    }
}
```

> For a shortcut that works from **every page**, register the action via `OmnisearchPlugin::make()->actions([...])` instead — global actions are always mounted.

---

Custom Scopes
-------------

[](#custom-scopes)

Implement `OmnisearchScope` to add any results you want. Each item must have a `type` of `url`, `action`, or `modal`.

### URL item — navigates to a page

[](#url-item--navigates-to-a-page)

```
use Ifsware\Omnisearch\Contracts\OmnisearchScope;

final class SettingsScope implements OmnisearchScope
{
    public function isActive(): bool
    {
        return true; // return false to disable this scope conditionally
    }

    public function getItems(string $query, array $context): array
    {
        return [
            [
                'id'       => 'settings.general',
                'type'     => 'url',
                'group'    => 'Settings',
                'title'    => 'General Settings',
                'subtitle' => 'Open the general settings page',
                'icon'     => 'heroicon-o-cog-6-tooth',
                'keywords' => ['settings', 'general', 'config'],
                'url'      => '/admin/settings',
            ],
        ];
    }
}
```

### Action item — runs a PHP callable server-side

[](#action-item--runs-a-php-callable-server-side)

```
[
    'id'       => 'actions.export',
    'type'     => 'action',
    'group'    => 'Actions',
    'title'    => 'Export Users',
    'subtitle' => 'Download all users as CSV',
    'icon'     => 'heroicon-o-arrow-down-tray',
    'keywords' => ['export', 'csv', 'download'],
    'action'   => fn () => \Artisan::call('export:users'),
]
```

### Modal item — opens a Livewire modal

[](#modal-item--opens-a-livewire-modal)

```
[
    'id'       => 'modal.invite',
    'type'     => 'modal',
    'group'    => 'Actions',
    'title'    => 'Invite User',
    'subtitle' => 'Open the invite user dialog',
    'icon'     => 'heroicon-o-user-plus',
    'keywords' => ['invite', 'user', 'add'],
    'modalId'  => 'invite-user-modal',
]
```

### Clipboard item — copies text to the clipboard

[](#clipboard-item--copies-text-to-the-clipboard)

```
[
    'id'       => 'clipboard.api-key',
    'type'     => 'clipboard',
    'group'    => 'Copy',
    'title'    => 'Copy API Key',
    'subtitle' => 'sk-••••••••••••••••',
    'icon'     => 'heroicon-o-clipboard',
    'keywords' => ['api', 'key', 'copy', 'token'],
    'text'     => $apiKey, // the actual value written to the clipboard
]
```

When selected, the value in `text` is written to the clipboard via the browser's Clipboard API. The item icon briefly changes to a checkmark, then the palette closes automatically.

`OmnisearchAction` also supports clipboard via the fluent API:

```
OmnisearchAction::make('copy-api-key')
    ->title('Copy API Key')
    ->subtitle('sk-••••••••••••••••')
    ->icon('heroicon-o-clipboard')
    ->keywords(['api', 'key', 'copy'])
    ->clipboard($user->api_key),
```

### Register the scope

[](#register-the-scope)

Add it to `config/omnisearch.php`:

```
'scopes' => [
    \Ifsware\Omnisearch\Scopes\OmnisearchNavigationScope::class,
    \Ifsware\Omnisearch\Scopes\OmnisearchResourceScope::class,
    \Ifsware\Omnisearch\Scopes\OmnisearchPanelScope::class,
    \Ifsware\Omnisearch\Scopes\OmnisearchActionScope::class,
    \Ifsware\Omnisearch\Scopes\OmnisearchPageActionsScope::class,
    \App\Omnisearch\SettingsScope::class, // your custom scope
],
```

---

Configuration
-------------

[](#configuration)

```
// config/omnisearch.php

return [
    // Set to false or OMNISEARCH_ENABLED=false in .env to disable entirely
    'enabled' => env('OMNISEARCH_ENABLED', true),

    // Active scopes — order determines result order in the palette
    'scopes' => [
        \Ifsware\Omnisearch\Scopes\OmnisearchNavigationScope::class,
        \Ifsware\Omnisearch\Scopes\OmnisearchResourceScope::class,
        \Ifsware\Omnisearch\Scopes\OmnisearchPanelScope::class,
        \Ifsware\Omnisearch\Scopes\OmnisearchActionScope::class,
        \Ifsware\Omnisearch\Scopes\OmnisearchPageActionsScope::class,
    ],

    // Maximum number of results returned across all scopes
    'max_results' => 50,

    // Keyboard shortcut to open the palette (mod = Cmd on Mac, Ctrl on Windows/Linux)
    'shortcut' => 'mod+k',

    // Set a string to override the placeholder for all locales,
    // or leave null to use the translation file (default).
    'placeholder' => null,

    // Group labels shown as section headers in the palette.
    // Leave null to use the translation file (default).
    'groups' => [
        'navigate' => ['label' => null, 'subtitle' => null],
        'actions'  => ['label' => null],
        'page'     => ['label' => null],
    ],

    'recent_searches' => [
        'enabled' => true,
        'label'   => null,      // leave null to use translation file (default)
        'max'     => 5,         // maximum number of recent searches to show
    ],

    'empty_state' => [
        'message'     => null,  // leave null to use translation file (default)
        'suggestions' => ['dashboard', 'users', 'settings'],
    ],
];
```

### Plugin options

[](#plugin-options)

```
OmnisearchPlugin::make()
    // Pass false to keep Filament's native global search bar visible
    ->disableDefaultGlobalSearch(false)
    ->actions([/* OmnisearchAction instances */]);
```

---

Multilingual Support
--------------------

[](#multilingual-support)

Filament Omnisearch ships with built-in translations for **English** (default), **Indonesian**, and **Dutch**. All UI strings — including placeholders, group labels, empty state messages, and footer shortcuts — are translatable.

### Publish translations

[](#publish-translations)

```
php artisan vendor:publish --tag="omnisearch-translations"
```

This copies the language files to `resources/lang/vendor/omnisearch/{en,id,nl}/`. Edit them to customize wording or add new locales.

### Override via config

[](#override-via-config)

You can still override specific strings globally via `config/omnisearch.php`. When a config value is set to a non-empty string, it takes precedence over the translation file.

```
'placeholder' => 'Type to search...',   // overrides translation file
'groups' => [
    'navigate' => ['label' => 'Go to', 'subtitle' => null], // mixed usage
],
```

### Adding a new language

[](#adding-a-new-language)

1. Copy `resources/lang/en/omnisearch.php`
2. Place it under `resources/lang/vendor/omnisearch/{locale}/omnisearch.php`
3. Translate the values

No code changes are required — Laravel's localization system will pick up the new locale automatically.

---

Keyboard Shortcuts
------------------

[](#keyboard-shortcuts)

### Built-in

[](#built-in)

KeyAction`⌘K` / `Ctrl+K`Open omnisearch`Escape`Close omnisearch`↑` / `↓`Navigate results`↵`Execute selected result### Custom action shortcuts

[](#custom-action-shortcuts)

Shortcuts defined via `->shortcut()` or the `shortcut` key in an item array are **active hotkeys** — pressing them executes the action directly without opening the palette first.

**Scope of custom shortcuts:**

Where definedActive`OmnisearchPlugin::make()->actions([...])`All pages, always`getOmnisearchActions()` on a pageOnly while on that page**Shortcut format** — use `+`-separated modifiers followed by a key:

```
mod+k          // ⌘K on Mac, Ctrl+K on Windows/Linux
mod+shift+u    // ⌘⇧U / Ctrl+Shift+U
alt+p          // ⌥P / Alt+P

```

`mod` resolves to `Cmd` on Mac and `Ctrl` on Windows/Linux. Other modifiers: `ctrl`, `meta`, `shift`, `alt`.

> **Avoid browser-reserved shortcuts.** Common conflicts: `mod+shift+n` (new incognito, Chrome), `mod+shift+p` (private window, Firefox), `mod+shift+j` (DevTools console), `mod+shift+i` (DevTools). Browser UI shortcuts are captured before the page sees them and cannot be overridden with `preventDefault()`.

---

Testing
-------

[](#testing)

```
composer test
```

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance96

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 87.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~27 days

Total

2

Last Release

20d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/26449835?v=4)[David H. Sianturi](/maintainers/davidhsianturi)[@davidhsianturi](https://github.com/davidhsianturi)

---

Top Contributors

[![solemanzoya](https://avatars.githubusercontent.com/u/31304756?v=4)](https://github.com/solemanzoya "solemanzoya (21 commits)")[![josuapsianturi](https://avatars.githubusercontent.com/u/54291811?v=4)](https://github.com/josuapsianturi "josuapsianturi (3 commits)")

---

Tags

filamentfilament-pluginlaravelsearch

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/ifsware-filament-omnisearch/health.svg)

```
[![Health](https://phpackages.com/badges/ifsware-filament-omnisearch/health.svg)](https://phpackages.com/packages/ifsware-filament-omnisearch)
```

###  Alternatives

[backstage/mails

View logged mails and events in a beautiful Filament UI.

16429.7k](/packages/backstage-mails)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3915.5k](/packages/rawilk-profile-filament-plugin)[filament/support

Core helper methods and foundation code for all Filament packages.

2333.9M304](/packages/filament-support)[stephenjude/filament-two-factor-authentication

Filament Two Factor Authentication: Google 2FA + Passkey Authentication

85240.3k10](/packages/stephenjude-filament-two-factor-authentication)[marcelweidum/filament-passkeys

Use passkeys in your filamentphp app

6758.2k2](/packages/marcelweidum-filament-passkeys)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
