How to Build Site Search with Astro, Qwik and Fuse.js

In this post, I’ll explain how to build a site search using Astro’s content collections, static endpoints and Qwik’s Astro integration with Fuse.js.

I’ve prepared a demo site and open source repo, which you’ll find at the following links:

Screenshot of demo site search

What Are Content Collections?

Astro has a convenient way to “bulk” query or transform content of similar types. In the case of my demo, this would apply to blog posts that are all written in MDX. All blog posts share the same template or layout and schema. Here’s the schema for blog posts.


You can see the src in the repo here: src/content/config.js.

And for good measure, here’s the frontmatter for one of my blog posts (but all blog posts will use the same schema).


You can see the src in the repo here: the-qwik-astro-audiofeed-experiment.mdx.

How to Query Astro’s Content Collections

To build site search functionality, I first need to query all the blog posts. I’ve achieved this using a static endpoint. I called it all-content.json.js and it lives in the src/pages directory. E.g.:


Once I’ve queried all the blog posts using getCollection('posts'), I do a quick filter to remove any blog posts that might be in draft mode, then return just the fields from the frontmatter that will be helpful for the search, and then sort them by date.

The result is stringified and returned as a standard Response.

Here’s what the result looks like.


You can see the src in the repo here: src/pages/all-content.json.js.

This data provides everything I’ll need to start building the search component.

How to Query a Static Endpoint

In order to build the search component (coming next!) I first need to query the data from the static endpoint and pass it on to the search component. I query the data in my layout component, which is present in each page of my demo site, E.g.:


You can see the src in the repo here: src/pages/index.astro.

And here’s the layout component which makes a server-side request to the endpoint.


One thing to point out here is the URL used in the fetch. If the site is deployed and PROD is true, the URL to the static endpoint will be https://tns-astro-site-search.netlify.app/all-content.json, and while in development the localhost URL is used.

Provided I am able to query the search data, I can pass it on to my search component via the data prop.

You can see the src in the repo here: src/layouts/layout.astro.

Building the Search Component

There are two additional dependencies to install in order to build the search component. They are as follows.

Fuse.js

I’ve used Fuse.js to help with the “fuzzy search.” Keyboard strokes are captured and passed through Fuse.js. If any of the letters or words match a title or date, Fuse.js will return the item.

Qwik

I use Qwik’s Astro integration to help manage client-side state. Qwik is more lightweight than React and is less verbose than vanilla JavaScript.

The remaining steps will cover how to set up the search and filtering. I’ve created a simple example, which you can preview here: https://tns-astro-site-search.netlify.app/simple. The src can be found here: src/components/simple-search.jsx.

Note: The example used in my demo contains a lot of additional CSS and JavaScript to handle the modal, which isn’t required to create search functionality.

Search Component: Step 1

The first step is to create the search component and return an HTML input. Add an onInput$ event handler and create a function named handleInput to capture the keystrokes.

Search Component: Step 2

Next import useSignal, and create two new constants to hold the values for all the data and the filtered data.

Search Component: Step 3

Next import and initialise Fuse.js. The config for Fuse.js accepts the value from the useSignal const (all.value) and will apply a fuzzy filter threshold of 0.5 when any input values match values for the title or date.

fuse.search can be used to filter out any items from the array that don’t meet the config parameters, and a new array is returned. I’ve called this new array “results.”

Search Component: Step 4

The next step is to add an if statement. If there’s a value captured from the HTML input, then I set useSignal filtered.value equal to the results, and if there’s no value captured from the HTML input then I set the useSignal filtered.value equal to the all.value.

This will either return a filtered list, or the whole list.

Search Component: Step 5

The final step is to iterate over the filtered.value (if it has length) and return a list of items. If there are no results, then I return null.

Finished

And that’s it, that’s all the principles behind how to query data using Astro’s content collections, how to make the data available using a static endpoint, and then implement fuzzy-search using Fuse.js and Qwik’s Astro integration to manage the client-side state.

I’ve used this same approach on my site, and it’s working out pretty well so far!

Group Created with Sketch.

 

 

 

 

Top