Searchbox | SSR Support
Build SSR enabled search experiences with @appbaseio/react-searchbox
SSR is a much talked about concept nowadays in the Web Development space. And it should be considering all the benefits it offers.
In this blog, we talk about how we are supporting SSR, requiring minimal config from the user in our component library @appbaseio/react-searchbox
to help build SEO friendly and performance optimized search apps.
Introduction to SSR
Server-side rendering (SSR), is the ability of an application to contribute by displaying the web page on the server instead of rendering it in the browser. The Server-side sends a fully rendered page to the client; the clientβs JavaScript bundle takes over and allows the SPA framework to operate.
The below graphics should convey the conceptual foundation of SSR and CSR.
This one should be more relatable :)
Benefits of SSR -
- The initial page of the website load is faster as there are fewer codes to render.
- Good for minimal and static sites.
- Search engines can crawl the site for better SEO.
Proof of concept πͺ
The claim SSR is better than CSR in terms of performance is proof backed. πͺ
We are attaching links to SSR and CSR versions of an application with concrete performance differences in terms of how they performed when tested with the Lighthouse tool.
--- attach examples here ---
The Library support for SSR
@appbase/react-searchbox
supports SSR with minimal user input.
Client-side
The user needs to provide just two props to the
<SearchBase />
component.contextCollector: used by our library util method to compute the initial state at the server side. (injected automatically).
initialState: the initial state of the app that is calculated at the server-side for hydration at client side.
const App = (props) => (
<SearchBase
index="good-books-ds"
credentials="a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61"
url="https://appbase-demo-ansible-abxiydt-arc.searchbase.io"
// below props are coming from the server
contextCollector={props.contextCollector}
initialState={props.initialState}
>
<SearchComponent
id="result-component"
highlight
dataField="original_title"
size={10}
>
{({ results, loading, size, setValue, setFrom }) => {
return (
<div className="result-list-container">
{loading ? (
<div>Loading Results ...</div>
) : (
<div>
{!results.data.length ? (
<div>No results found</div>
) : (
<p>
{results.numberOfResults} results found in {results.time}ms
</p>
)}
{results.data.map((item) => (
<div
className="book-header"
dangerouslySetInnerHTML={{
__html: item.original_title
}}
/>
))}
</div>
)}
</div>
);
}}
</SearchComponent>
</SearchBase>
);
export default App;
- Server-Side
On the server-side code, the user imports a util method getServerResults()(..., ...)
to compute the initial state of the App and passes this initial state back to the client-side.
getServerResults()(App, pageURL): the first param of the function receives the App
component ref and the second param[optional] receives the URL string or query param object(should be parsed) to respect the URL query string.
Assuming Next.js used for SSR here.
import { getServerResults } from '@appbaseio/react-searchbox';
// getServerSideProps method is run on server-side by Next.js
export async function getServerSideProps(context) {
// calculating the initial state on server
let initialState = await getServerResults()(App, context.resolvedUrl);
return {
props: { initialState } // will be passed to the App component as props
};
}
π That's it π₯³
The Underlying Architecture π·π π
Let's understand the underlying architecture and how this support is made possible.
The basic idea of SSR support for @appbaseio/react-searchbox
is to perform any necessary API calls to the search client and compute the initial state of App, then redyhrate the client side with the initialState computed on the server-side.
To build the initial state of the app at server-side, the getServerResults()(App)
renders the app tree on server side using ReactDOMServer.renderToStaticMarkup()
method and injects the contextCollector
.
The contextCollector
is a method that collects the context of the App
being rendered.
Based on the collected context, the relevant API calls to the search client are made and the initial state of the client-side app is computed.
This initialState
is returned to the user.
// server-side code
let initialState = await getServerResults()(App, context.resolvedUrl);
// user passes this initialState to the client and injects it to <SearchBase /> as a prop
The user then has to just pass this initialState
to the client-side and inject it in the <SearchBase />
component.
And that's it. Period.
Why we chose this architecture approach? π§
Our focus while integrating support for SSR in our library has been to minimize efforts from the user.
As you might notice, with just two props, to the <SearchBase />
i.e., contextCollector
and initialState
you have a search application giving you a performant search experience.
Notice how we handle all the API calls ourselves behind the scenes and also the configuration is as less as possible. π
In a nutshell, we bake everythingπ©βπ³. Leaving the consumption π€€ to the user.
Action Time π¦Έ π π€ π₯·
Here's a fully working example made using Next.js.
Check out another example we made using Express.js here.
Summary
Let's summarize what we have covered in this blog.
- What SSR?
- Why SSR?
- How to enable SSR in your search app built using
@appbase/react-searchbox
. - The mechanism making SSR support possible.
- Examples using Next.js and Express.js.