March 27, 2024 · 4 min read · 213 views
In this blog post, we'll explore how to enhance the search functionality of your Statamic site by integrating Fuse.js with Alpine.js. Fuse.js is a powerful lightweight JavaScript library for fuzzy searching, making it an excellent choice for implementing advanced search features on your website. When combined with Alpine.js and Statamic, you can create a seamless and responsive search experience for your users. This is useful if you have an SSG site.
Before we begin, ensure that Alpine.js is installed in your project. You can add it along with Fuse.js and Axios, which we'll use for fetching data, by running the following commands:
npm install alpinejs fuse.js axios
We'll be working within the Site.js
file, where we construct the logic to display search results. Alpine.js will be our tool for managing the reactive components and user input.
To utilize Fuse.js for searching, we need a dataset to search against. This dataset should be in a JSON format, accessible to our JavaScript code. In our case, we'll be using a JSON file that contains all the searchable content of our Statamic site.
Before we can perform any search operations, we need to generate the /search/data.json
file. This file will hold the data that Fuse.js will search through, such as titles, categories, and tags of all posts. To automate the creation and updating of this JSON file, we can utilize Statamic's event system, specifically the EntrySaved
event.
For detailed instructions on how to set up an event listener to generate and refresh the /search/data.json
file, refer to our blog post Event Listeners for Statamic. There, we explain how to create a custom event listener that triggers whenever a new post is added or an existing one is updated, ensuring that your search data is always current.
Here's how to set up the search functionality:
php artisan make:listener SavePostsToJson
Inside the SavePostsToJson.php
it should look like this
<?php
namespace App\Listeners;
use Statamic\Facades\Entry;
use Statamic\Events\EntrySaved;
class SavePostsToJson
{
public function handle(EntrySaved $event)
{
$data = Entry::query()
->where('collection', 'news')
->get()
->transform(function ($post) {
return [
'title' => $post->title,
'url' => $post->url,
// Searches for relevant information based on the given query string.
];
})
->toArray();
file_put_contents(public_path() . '/search/data.json', json_encode($data));
}
}
Modify the EventServiceProvider.php
file to include the created event listener:
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\Listeners\SavePostsToJson;
use Statamic\Events\EntrySaved;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
EntrySaved::class => [
SavePostsToJson::class,
]
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
}
Navigate to the "public" folder in your directory.
Create a new folder within "public" and name it "search".
Inside the "search" folder, create a file named "data.json".
import Alpine from "alpinejs";
import axios from "axios";
import Fuse from "fuse.js";
window.Alpine = Alpine;
Alpine.data("search", () => ({
fuse: null,
search: null,
results: [],
init() {
axios(`/search/data.json`).then((response) => {
this.fuse = new Fuse(response.data, {
includeScore: true,
threshold: 0.4,
keys: ["title"],
});
});
this.$watch("search", () => {
let data = this.fuse.search(this.search);
data = data
.sort((resultA, resultB) => resultA.score - resultB.score)
.map(({ item }) => {
return {
title: item.title,
url: item.url,
};
});
this.results = data;
});
},
}));
Alpine.start();
In the above script, we've initialized Alpine.js with a search
component, which fetches our search data and sets up Fuse.js. It also watches the searchQuery
property for changes and updates the results
array with the sorted search results accordingly.
Define the keys for the properties you wish to search through. In this example, we're searching the 'title' property only, but you can add more properties to the keys array as needed.
The search interface is defined in the markup as follows:
<div x-data="search">
<input type="text" name="search" x-model="search" placeholder="Search">
<div>
<template x-for="item in results">
<a x-bind:href="item.url">
<p x-text="item.title"></p>
</a>
</template>
<template x-if="search && results.length === 0">
<p>No Results For <span x-text="search"></span>.</p>
</template>
</div>
</div>
In this snippet, we've created an input field bound to the searchQuery
model. We've also set up conditional rendering for when there are search results or when no results are found. The x-for
directive loops through the results
array, displaying each result as a clickable link.
By following these steps, you've now equipped your Statamic site with an enhanced search feature that provides instant feedback and a better user experience. Make sure to check how you can use more advanced searching integrations such as Algolia, Meilisearch, and Elasticsearch.
Are you planning a new Statamic project or thinking about migrating your WordPress site to Statamic? Learn more about our expertise as a renowned Statamic development agency.
Technologies:
Related Posts
Stay up to date
Be updated with all news, products and tips we share!