Getting started with Laravel Spotlight

On my Mac, I always enjoy how quickly I can open applications, run commands and navigate through the OS via the command palette known as Spotlight. So I thought how great it would be to have this on the web as well. So I went ahead and got going to build Laravel Spotlight for one of my projects, Unlock. A few days later, I published the repository, and the response has been amazing.

This tutorial will show you how you can use Laravel Spotlight to enhance your application and improve the user experience. Fair warning, once you implement this, chances are you will want to use this for all your applications ;)

Installation

Installing Laravel Spotlight only takes a few minutes. Let's start by downloading the package from Composer:

composer require wire-elements/spotlight

Next, you will need to add the @livewire directive to your main template file like views/resources/layouts/app.blade.php:

@livewire('livewire-ui-spotlight')

@livewireScripts

</body>
</html>

Laravel Spotlight requires Alpine, so you can either add this to your package.json or include the minified version from the UNPKG CDN:

<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>

Finally, let's publish the configuration file, which I will reference in future steps.

php artisan vendor:publish --tag=livewire-ui-spotlight-config

Opening Spotlight

You should now see the command prompt popup once you use one of the following shortcuts:

  • CTRL + K
  • CMD + K
  • CTRL + /
  • CMD + /

You can adjust these shortcuts in the livewire-ui-spotlight.php config file:

<?php

return [

    /*
    |--------------------------------------------------------
    | Shortcuts
    |--------------------------------------------------------
    |
    | Define which shortcuts will activate Spotlight CTRL / CMD + key
    | The default is CTRL/CMD + K or CTRL/CMD + /
    |
    */

    'shortcuts' => [
        'k',
        'slash',
    ],

Additionally, you can also toggle Spotlight by using browser events from either other Livewire components or by using Alpine's $dispatch helper:

<?php

class MyComponent extends Component {
    public function openSpotlight()
    {
        $this->dispatchBrowserEvent('toggle-spotlight');
    }
}
<button @click="$dispatch('toggle-spotlight')">Toggle Spotlight</button>

Creating your first Spotlight command

Now that we have the basics covered, we can continue with creating our very first Spotlight command. The possibilities are endless, but one of the easiest and most productive commands is a command that helps you navigate to different parts of your application, e.g., viewing the details of a specific user.

So let's start by using the make:spotlight command and create our command:

php artisan make:spotlight ViewUser

The artisan:make command will create a new class, open app\Spotlight\ViewUser.php and define the name and description of our command:

<?php

namespace App\Spotlight;

use LivewireUI\Spotlight\SpotlightCommand;

class ViewUser extends SpotlightCommand
{
    protected string $name = 'View User';

    protected string $description = 'View a given user';
    
    //
}

The command will not show up until we've registered it, so let's do this right now. Spotlight supports two different approaches to register commands; you can either add your command to the livewire-ui-spotlight.php file:

<?php

return [
    
    //
    
    'commands' => [
        \LivewireUI\Spotlight\Commands\Logout::class
    ],

];

Alternatively, you can register commands from your service provider:

<?php

namespace App\Providers;


use Illuminate\Support\ServiceProvider;


class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Spotlight::registerCommand(ViewUser::class);
    }
}

One benefit of using the ServiceProvider is the ability to register commands conditionally:

Spotlight::registerCommandIf(true, ViewUser::class);
Spotlight::registerCommandUnless(false, ViewUser::class);

In some cases, you might want to show or hide commands based on the user; this can be achieved with the shouldBeShown method, which we will discuss shortly.

If you open Spotlight and start typing, you will see the ViewUser command in the list of commands:

View User Spotlight Command

Defining Spotlight dependencies

Commands can include dependencies like the one we are building right now. When referring to dependencies, I mean additional arguments you would like to ask from the user. In this case, we want to search for available users to view.

To define dependencies, we need to provide Spotlight with a SpotlightCommandDependencies collection. Let's take a look:

class ViewUser extends SpotlightCommand
{
    protected string $name = 'View User';

    protected string $description = 'View a given user';

    public function dependencies(): ?SpotlightCommandDependencies
    {
        return SpotlightCommandDependencies::collection()
            ->add(
                SpotlightCommandDependency::make('user')
                ->setPlaceholder('Which user do you want to view?')
            );
    }

We register our dependencies by creating a public function called dependencies which will return an instance of SpotlightCommandDependencies. You can register an unlimited number of placeholders, but we will stick with just one in this example. Our dependency is called user, which we will reference later to search and get the associated value. We also provided a placeholder which the command prompt will show when asking for the user dependency.

Now that our command is aware of the dependency, we need to tell it how to search and resolve users to view. We can do this by creating a new method called searchUser, the second part of this method must match the name of your dependency. The IoC container will resolve any method parameters. For our ViewUser command, we want to get the input query from our user. To do this, you just add $query as the searchUser method argument:

    public function searchUser($query)
    {
        return User::where('name', 'like', "%$query%")
            ->get()
            ->map(function(User $user) {
                return new SpotlightSearchResult(
                    $user->id,
                    $user->name,
                    sprintf('View user %s', $user->name)
                );
            });
    }

You can use any way you like to search; in this example, we use Eloquent to search our MySQL database, but you could also use Laravel Scout, for example. Search results must be mapped into a SpotlightSearchResult instance. This class requires three arguments, a unique identifier for the search result, a value to display, and optionally a description.

Autocomplete to select the user to view

If you have multiple dependencies, you will have access to every previous dependency to allow scoped searching. For example:

return SpotlightCommandDependencies::collection()
            ->add(
                SpotlightCommandDependency::make('user')
                ->setPlaceholder('Which user do you want to view?')
            )
            ->add(
                SpotlightCommandDependency::make('foobar')
                ->setPlaceholder('Search for second dependency')
            );

Now, the foobar dependency will have access to the user dependency:

    public function searchFoobar($query, User $user)
    {
        // Given Foobar is the second dependency it will have access to any resolved depedencies defined earlier. In this case we can access the User which was chosen.
    }

Executing commands

Once the user provides all our dependencies, we can continue and execute our command. We can do this by creating a new method called execute . This method resolves any dependencies from the Laravel IoC container, including our command dependencies:

public function execute(User $user)
{
    dd($user); // Returns the resolved User modal.
}

Now that we have our User modal, we can redirect the user to the user profile:

public function execute(User $user)
{
    return redirect()->route('users.show', $user);
}

In some cases, you might want to interact with the Spotlight Livewire component itself. For example, you might want to integrate your command with the Wire Elements modal package:

In order to do this, we need to inject the Spotlight Livewire component instance and emit the openModal event to which the modal is listening:

use LivewireUI\Spotlight\Spotlight;

public function execute(Spotlight $spotlight, User $user)
{
    $spotlight->emit('openModal', 'view-user', ['user' => $user->id]);
}

And that's it, just hit your Spotlight shortcut, choose the "View user" command, enter the name of the user, and hit enter to see the modal appear.

I've already seen people implement Spotlight days after the initial launch πŸ˜„

I'm looking forward to seeing what you are building with Laravel Spotlight πŸš€ Be sure to share your result with me on Twitter.


I'm thinking about releasing a premium video course in the near future (if enough people are interested) where I will show you how to build a real-world application from start to finish using Laravel, Livewire, and Tailwind CSS. I will be covering everything, from registering a domain, setting up a server, writing tests, you name it. Subscribe if you are interested and want to be notified, and you might also get access for free as I will be doing a giveaway when the course launches.