react-instantsearch-router-nextjs breaks routing
stevebutler2210 opened this issue · comments
Description
When using Typesense with Next JS and React Instantsearch, attempting to use the react-instantsearch-router-nextjs
package to create a router for the InstantSearch
routing
prop causes the application to break (when searching, filtering etc, the params briefly appear in the URL but are then cleared down, resetting the search to the default results).
Without this setup, I am getting the following warning in the console
[InstantSearch] You are using Next.js with InstantSearch without the "react-instantsearch-router-nextjs" package.
This package is recommended to make the routing work correctly with Next.js.
Please check its usage instructions: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch-router-nextjs
What is more problematic is that this package / setup is required to achieve SSR with InstantSearch and Next JS (see here).
Is there any other way to achieve SSR with Next / Typesense, without using the react-instantsearch-router-nextjs
routing?
Steps to reproduce
Example of a Next JS page component where the described issue can be observed. I am using the sample books data / schema etc from the Typesense Building A Search Application docs
import singletonRouter from "next/router";
import {
InstantSearch,
SearchBox,
Hits,
Pagination,
RefinementList,
SortBy,
} from "react-instantsearch";
import { createInstantSearchRouterNext } from "react-instantsearch-router-nextjs";
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
server: {
apiKey: "YOUR_API_KEY", // Be sure to use the search-only-api-key
nodes: [
{
host: "YOUR_CLUSTER.typesense.net",
port: 443,
protocol: "https",
},
],
},
// The following parameters are directly passed to Typesense's search API endpoint.
// So you can pass any parameters supported by the search endpoint below.
// queryBy is required.
additionalSearchParameters: {
query_by: "title,authors",
},
});
const searchClient = typesenseInstantsearchAdapter.searchClient;
const transformItems = (items) => {
return items.map((item) => ({
...item,
count: `(${item.count})`,
}));
};
const SearchPage = () => {
return (
<InstantSearch
searchClient={searchClient}
indexName="books"
routing={{
router: createInstantSearchRouterNext({
singletonRouter,
}),
}}
future={{
preserveSharedStateOnUnmount: true,
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
gap: "50px",
fontSize: "24px",
}}
>
<div style={{ width: "50%" }}>
<SearchBox />
<Hits
hitComponent={({ hit }) => (
<div>
<h6>{hit.title}</h6>
<p>{hit.authors.join(", ")}</p>
<p>{hit.publication_year}</p>
</div>
)}
/>
<Pagination />
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<p>Publication Years</p>
<RefinementList
attribute="publication_year"
transformItems={transformItems}
sortBy={["name"]}
/>
</div>
<SortBy
items={[
{ label: "Relevance", value: "books" },
{ label: "Title", value: "books/sort/title:asc" },
{
label: "Publication Year",
value: "books/sort/publication_year:desc",
},
]}
defaultValue={"title"}
/>
</div>
</InstantSearch>
);
};
export default SearchPage;
Expected Behavior
The params from the search / filters / sorting etc should be persisted correctly, without being cleared down.
Actual Behavior
The route updates, clearing all params, shortly after taking any action that affects it
Screen.Recording.2024-04-16.at.12.14.17.mov
Metadata
Typesense Version: 1.8.2
OS: MacOS 14.4.1
Next JS Version: 14.1.0
react-instantsearch Version: 7.7.1
react-instantsearch-router-nextjs Version: 7.7.1
The typesense-instantsearch-adapter is a data transformation library that Instantsearch calls with a set of search parameters, which the adapter translates to the Typesense API format, and then translates the API response from Typesense to a format the widgets understand.
So any UI / routing behaviors are almost always outside the control of the typesense adapter itself.
I would recommend checking if you're able to replicate the issue with an Algolia index, and if so, report the issue in the Algolia Instantsearch repo directly.
If it works properly with an Algolia index, and the issue only exists in Typesense, could you share GitHub links to both the Algolia and Typesense versions, and I can take a closer look to see if it's a data transformation issue that's causing any UI issues as a side-effect.
Is this with app router or pages? Because react-instantsearch-router-nextjs
and InstantSearch
is for the pages directory. For the app router you have to use InstantSearchNext
from react-instantsearch-nextjs
. Docs
Thanks @thedevdavid, yeah this is using the pages router 👍 @jasonbosco I'll try that out and get back to you when I've had a chance to take a better look. I can see the showcase demo is configured for SSR, so that at least tells me what I'm doing should be possible—my gut feel is I've made a silly mistake somewhere and I'm just not seeing it at the moment. Will report back
Ultimately, the issue was caused by something completely different—skimming Algolia issues I came across this, which sounded awfully like the behaviour I was seeing.
After a lot of poking around, I ended up looking at our _app.tsx
page, which looked like:
import type { AppProps } from "next/app";
import { useRouter } from "next/router";
import { ApolloProvider } from "@apollo/client";
import { AnimatePresence } from "framer-motion";
import { appWithTranslation } from "next-i18next";
import { graphqlClient } from "@/lib/graphqlClient";
import "@/styles/globals.scss";
import "@/styles/theme.scss";
const App = ({ Component, pageProps }: AppProps) => {
const client = graphqlClient();
const router = useRouter();
return (
<ApolloProvider client={client}>
<AnimatePresence
mode="wait"
initial={false}
onExitComplete={() => window.scrollTo(0, 0)}
>
<Component {...pageProps} key={router.asPath} />
</AnimatePresence>
</ApolloProvider>
);
};
export default appWithTranslation(App);
Simply removing the key
prop from Component
completely fixes the issues I was experiencing!
I'm going to dig into the exact cause here a little deeper, but I'll close this issue now as it was clearly unrelated to Typesense. Apologies for clogging up the inbox!