I have a website with a search on it. I'd like the user to be able to click on a search result and open the product in an overlay instead of reloading the whole page, however I would like the url in the url bar to be the correct product page so that is a user copies and pastes it or opens it in a new tab they'll get the product page. I'd also like the back button to work in both cases.
Here's a a more detailed explanation of the process.
I thought at first that I might be able to use the {shallow:true} option on Next/Router to get this to work but the docs specifically say this only works for same page navigation (with a different query string).
My only idea is that I may be able to set the link up as a proper but hijack it in the browser to do something in pure javascript in the history state but I'm not sure whether the Next Router can be removed from the equation.
Just to be clear, I'm not asking for help with the product page or overlay itself, I can easily create shared components that show the same content in both situations, it's specifically the routing and URL issues I'm requesting help with.
Any help, even just a pointer in the right direction would be really appreciated.
Here is how I did it.
You'll need to create an optional catch all route. i.e: pages/search/[[searchParams]].tsx
import { useRouter } from 'next/router';
import { GetServerSidePropsContext } from 'next';
import { ProductsView, ProductDetailsView } from '@/views';
export async function getServerSideProps({
params,
}: GetServerSidePropsContext) {
const [productId = null] = (params?.searchParams as string[]) || [];
return {
props: {
productId,
},
};
}
interface Props {
productId?: string;
}
function SearchProductsPage({ productId }: Props) {
const router = useRouter();
// You could use next/link in your views instead of using next/router
const onViewDetails = (productDetailsId: string) => {
router.push(productDetailsId, `product/${productDetailsId}`).catch((e) => {
console.error(e);
});
};
// Going back to /search will remove the productId and the overlay view will be removed
const onClose = () => {
router.push('/search').catch((e) => {
console.error(e);
});
};
// If you have a productId (catch from URL and received via Props)
// it will render the Product details view which contains the overlay and the product details
return (
<>
<ProductsView onViewDetails={onViewDetails} />
{productId && <ProductDetailsView productId={productId} onClose={onClose} />}
</>
);
}
export default SearchProductsPage;
Please note that routing to /product/${productDetailsId}
instead of product/${productDetailsId}
will give you an error. So you with this code, you'll end up with URL like /search/product/1
instead of /product/1
.
But you can create a separate page i.e. pages/product/[productId].tsx
that renders a product details view, not in an overlay.
I have made a code sandbox for you, hope that helps.
https://codesandbox.io/s/overlay-4qhfb-4qhfb
We have used two React Hooks, one to keep state and one to handle the event. We used window.history.pushState to handle the URL
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With