🏝 If you like the content of this blog, I would really appreciate your vote in Tuple's Send an Open Source Developer on Vacation contest. Just search for "Freek Van der Herten" in the nominees list and cast your vote. 🙌

😀 This holiday won't only be a nice reward for me, but also for my girlfriend who always gives me a lot of time working on open source packages and blog posts.

How to add a spotlight-like search field to your Laravel app

Original – by Freek Van der Herten – 6 minute read

As developers, we tend to like shortcuts to speed up our workflow. One of the tools I'm using to speed up doing stuff on my Mac is Raycast. It offers a lovely command palette that allows opening apps and URLs, working with clipboard history, and much more.

Wouldn't it be nice to also add such a command palette to a Laravel app? This way, power users of your app can get around quickly and perform small tasks without having to click around.

The good news is that there's already a package to add such a thing: Spotlight by Philo Hermans. In this blog post, I'd like to show how we use this fantastic package at Oh Dear.

How users can use the command palette

Oh Dear is an all-in solution for monitoring your website. It is simple to use, but because we offer a lot of checks, our UI is quite extensive: there are many screens.

To make navigating the service a little bit easier, we added the command palette provided by the Spotlight package.

When you log in Oh Dear (you can create a trial account if you don't already have one), you'll notice a new little search field in the header.

image

When you click that, it opens up a nice command palette. You can use this to navigate to anywhere in our service quickly.

image

If you want to go to the performance graph of your site, just type "performance", ...

image

... and type the site's name you want to see the performance results of.

image

image

When selecting the site, you'll get taken to the performance results.

image

In many cases, this is much faster than clicking on the site and then going to the right check results yourself.

The command palette has commands to navigate to almost anywhere in our UI, and tasks like logging out or switching teams.

image

Using the Spotlight package

Using the Spotlight package is simple. You can install it with Composer. Just keep in mind that it requires Livewire.

composer require wire-elements/spotlight

Next, you should include the provided Livewire component in your view.

<html>
<body>
<!-- content -->

@livewire('livewire-ui-spotlight')
</body>
</html>

With that out of the way, you can start writing Spotlight command classes. Here is what our command class for logging out the user looks like.

namespace App\Support\SpotlightCommands;

use App\Domain\Site\Models\Site;
use LivewireUI\Spotlight\Spotlight;
use LivewireUI\Spotlight\SpotlightCommand;

class LogoutCommand extends SpotlightCommand
{
    public string $name = 'Logout';

    public string $description = 'Log out from your account';

    public function execute(Spotlight $spotlight, Site $site): void
    {
        $spotlight->redirect(route('logout'));
    }
}

The name property contains the string a user needs to type to invoke the command. The Execute function will get executed when the user picks that function. As mentioned in the docs, Spotlight commands should be registered in the config file or a service provider.

And that is all you need to do to offer a command. Pretty cool, right?

Let me show you one more command. In the previous section, you saw that when a user wants to see a site's performance results, we ask the user which site they want to see it. Such a question is called "a dependency" in Spotlight parlance.

Here's what that ViewPerformanceCheckResultsCommand looks like.

namespace App\Support\SpotlightCommands;

use App\Domain\Site\Models\Site;
use Illuminate\Support\Collection;
use LivewireUI\Spotlight\Spotlight;
use LivewireUI\Spotlight\SpotlightCommand;
use LivewireUI\Spotlight\SpotlightCommandDependencies;
use LivewireUI\Spotlight\SpotlightCommandDependency;
use LivewireUI\Spotlight\SpotlightSearchResult;

class ViewPerformanceCheckResultsCommand extends SpotlightCommand
{
    protected string $name = 'Performance check results';

    public function dependencies(): ?SpotlightCommandDependencies
    {
        return SpotlightCommandDependencies::collection()
            ->add(
                SpotlightCommandDependency::make('site')
                    ->setPlaceholder('For which site?')
                    ->setType(SpotlightCommandDependency::SEARCH)
            );
    }

    public function searchSite(string $query): Collection
    {
        return currentTeam()
            ->sites()
            ->search($query)
            ->limit(15)
            ->get()
            ->map(function (Site $site) {
                return new SpotlightSearchResult(
                    $site->id,
                    $site->name,
                    ''
                );
            });
    }

    public function execute(Spotlight $spotlight, Site $site): void
    {
        $url = route('check.performance', $site);

        $spotlight->redirect($url);
    }
}

So in dependencies you can define which follow up questions should be asked when a user picks this command. If it is a question that involves searching something, you should add a function called search<name of the dependency>, in our case, searchSite.

This will let Spotlight display that "For which site?" question. Users can type the name of the site. When a site gets picked, the chosen site will be passed to execute, and we can redirect the user to the correct route.

Because we have a lot of spotlight commands that will ask for a site and then redirect to a page, I extracted most of the logic away in an abstract SiteSpotlightCommand.

namespace App\Support\SpotlightCommands;

use App\Domain\Site\Models\Site;
use App\Support\SpotlightCommands\Concerns\HasSiteDependency;
use Illuminate\Support\Collection;
use LivewireUI\Spotlight\Spotlight;
use LivewireUI\Spotlight\SpotlightCommand;
use LivewireUI\Spotlight\SpotlightCommandDependencies;
use LivewireUI\Spotlight\SpotlightCommandDependency;
use LivewireUI\Spotlight\SpotlightSearchResult;

abstract class SiteSpotlightCommand extends SpotlightCommand
{
    public function dependencies(): ?SpotlightCommandDependencies
    {
        return SpotlightCommandDependencies::collection()
            ->add(
                SpotlightCommandDependency::make('site')
                    ->setPlaceholder('For which site?')
                    ->setType(SpotlightCommandDependency::SEARCH)
            );
    }

    public function searchSite(string $query): Collection
    {
        return currentTeam()
            ->sites()
            ->search($query)
            ->limit(15)
            ->get()
            ->map(function (Site $site) {
                return new SpotlightSearchResult(
                    $site->id,
                    $site->label,
                    '',
                );
            });
    }

    public function execute(Spotlight $spotlight, Site $site): void
    {
        $url = $this->getUrl($site);

        $spotlight->redirect($url);
    }

    abstract protected function getUrl(Site $site): string;
}

This is what is left over in the ViewPerformanceCheckResultsCommand.

namespace App\Support\SpotlightCommands;

use App\Domain\Site\Models\Site;

class ViewPerformanceCheckResultsCommand extends SiteSpotlightCommand
{
    protected string $name = 'Performance check results';

    protected function getUrl(Site $site): string
    {
        return route('check.performance', $site);
    }
}

With this set up, adding other spotlight commands to navigate to a different part of the UI becomes a breeze.

In closing

I hope you like this little tour of how we are using the Spotlight package at Oh Dear.

Philo has done an excellent job on the package. If you want to see more, check out his talk he gave at the Laravel Worldwide Meetup.

Stay up to date with all things Laravel, PHP, and JavaScript.

Follow me on Twitter. I regularly tweet out programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "How to add a spotlight-like search field to your Laravel app"?

Want to join the conversation? Log in or create an account to post a comment.