useSiteSearch
is a React hook that you can use to render a DatoCMS Site Search widget.
The hook only handles the form logic: you are in complete and full control of how your form renders down to the very last component, class or style.
To perform the necessary API requests, this hook requires a DatoCMS CMA Client instance, so make sure to also add the following package to your project:
npm install --save react-datocms @datocms/cma-client-browser
Import useSiteSearch
from react-datocms
and use it inside your components like this:
import { useSiteSearch } from 'react-datocms';
import { buildClient } from '@datocms/cma-client-browser';
const client = buildClient({ apiToken: 'YOUR_API_TOKEN' });
const { state, error, data } = useSiteSearch({
client,
buildTriggerId: '7497',
// optional: by default fuzzy-search is not active
fuzzySearch: true,
// optional: you can omit it you only have one locale, or you want to find results in every locale
initialState: { locale: 'en' },
// optional: to configure how to present the part of page title/content that matches the query
highlightMatch: (text, key, context) =>
context === 'title' ? (
<strong key={key}>{text}</strong>
) : (
<mark key={key}>{text}</mark>
),
// optional: defaults to 8 search results per page
resultsPerPage: 10,
});
For a complete walk-through, please refer to the DatoCMS Site Search documentation.
prop | type | required | description | default |
---|---|---|---|---|
client | CMA Client instance | ✅ | DatoCMS CMA Client instance | |
buildTriggerId | string | ✅ | The ID of the build trigger to use to find search results | |
fuzzySearch | boolean | ❌ | Whether fuzzy-search is active or not. When active, it will also find strings that approximately match the query provided. | false |
resultsPerPage | number | ❌ | The number of search results to show per page | 8 |
highlightMatch | (match, key, context: 'title' | 'bodyExcerpt') => React.ReactNode | ❌ | A function specifying how to highlight the part of page title/content that matches the query | (text, key) => (<mark key={key}>{text}</mark>) |
initialState.query | string | ❌ | Initialize the form with a specific query | '' |
initialState.locale | string | ❌ | Initialize the form starting from a specific page | 0 |
initialState.page | string | ❌ | Initialize the form with a specific locale selected | null |
The hook returns an object with the following shape:
{
state: {
query: string;
setQuery: (newQuery: string) => void;
locale: string | undefined;
setLocale: (newLocale: string) => void;
page: number;
setPage: (newPage: number) => void;
},
error?: string,
data?: {
pageResults: Array<{
id: string;
title: React.ReactNode;
bodyExcerpt: React.ReactNode;
url: string;
raw: RawSearchResult;
}>;
totalResults: number;
totalPages: number;
},
}
- The
state
property reflects the current state of the form (the currentquery
,page
, andlocale
), and offers a number of functions to change the state itself. As soon as the state of the form changes, a new API request is made to fetch the new search results; - The
error
property returns a string in case of failure of any API request; - The
data
property returns all the information regarding the current search results to present to the user;
If both error
and data
are null
, it means that the current state for the form is loading, and a spinner should be shown to the end user.
This example uses the react-paginate
npm package to simplify the handling of pagination:
import { buildClient } from '@datocms/cma-client-browser';
import ReactPaginate from 'react-paginate';
import { useSiteSearch } from 'react-datocms';
import { useState } from 'react';
const client = buildClient({ apiToken: 'YOUR_API_TOKEN' });
function App() {
const [query, setQuery] = useState('');
const { state, error, data } = useSiteSearch({
client,
initialState: { locale: 'en' },
highlightMatch: (text, key, context) =>
context === 'title' ? (
<strong key={key}>{text}</strong>
) : (
<mark key={key}>{text}</mark>
),
buildTriggerId: '7497',
resultsPerPage: 10,
});
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
state.setQuery(query);
}}
>
<input
type="search"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<select
value={state.locale}
onChange={(e) => {
state.setLocale(e.target.value);
}}
>
<option value="en">English</option>
<option value="it">Italian</option>
</select>
</form>
{!data && !error && <p>Loading...</p>}
{error && <p>Error! {error}</p>}
{data && (
<>
{data.pageResults.map((result) => (
<div key={result.id}>
<a href={result.url}>{result.title}</a>
<div>{result.bodyExcerpt}</div>
<div>{result.url}</div>
</div>
))}
<p>Total results: {data.totalResults}</p>
<ReactPaginate
pageCount={data.totalPages}
forcePage={state.page}
onPageChange={({ selected }) => {
state.setPage(selected);
}}
activeClassName="active"
renderOnZeroPageCount={() => null}
/>
</>
)}
</div>
);
}