This post documents adding search to next.js powered static sites using fuse.js. When deciding how to implement search functionality, I ended up choosing between a custom implementation (basically just filtering arrays) and using a dedicated library. While I tend not to reach for off the shelf solutions, I chose fuse because it has UMD and commonJS support and will likely be helpful to eventually build out the cli.
- initialize fuse to search through the posts
- hook up the search function to text input
- return filtered list of posts
initialize fuse
I'm adding search to the garden page because it already has access to all the post data needed to actually search. I have an array of posts with the following shape:
post = {content,frontmatter: {tags: [],title}}
Because it's searching a nested list, we need to tell fuse which fields to search for:
const options = {keys: ['content', 'frontmatter.title', 'frontmatter.tags']};const fuse = new Fuse(posts, options);
hook up search input
We'll use a hook to keep track of the search value. The one extra piece here is debouncing the input. I like this a bit better than throttling since I'm guessing users will be entering single words rather than phrases.
import debounce from 'lodash.debounce';const [searchVal, setSearchVal] = useState('');const debounceQuery = debounce(val => setSearchVal(val), 333);const updateSearchVal = e => {const { value } = e.target;debounceQuery(value);};...<input value={searchVal} onChange={updateSearchVal} />
return filtered list of posts
Last thing to do is filter the posts based on the search criteria. The else
statement resets the postState
back to its initial state when the search field is empty.
const [postState, setPostState] = useState(posts);useEffect(() => {if (searchVal) {const filteredPosts = fuse.search(searchVal).map(p => p.item);setPostState(filteredPosts);} else {setPostState(posts);}}, [searchVal]);
wrapping up
A tribute to fuse, to be sure, this was extremely easy. This logic will need to be extended at some point to do tag filtering, too, but that can wait until there's more content.
One thing that came up while working on this was the lack of syntax highlighting -- something I wholesale forgot -- so I'll be adding that next.
up next
- syntax highlighting
- content directories
- templating