Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open a page in a Next.js website as an overlay

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.

enter image description here

Here's a a more detailed explanation of the process.

  • User navigates to the list page at /list?keywords=widget
  • User clicks a product
  • The contents of the product animate in on an overlay but the list page doesn't unload, it's still below the product page
  • The URL bar updates to reflect that the user is on /products/123 but the products page isn't actually loaded.
  • If the user hits the back button then the overlay disappears.
  • If the user hits the refresh button while the overlay is open they will get an ssr version of the product page, the list page underneath is no longer there.

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.

like image 987
jonhobbs Avatar asked Dec 26 '20 19:12

jonhobbs


Video Answer


2 Answers

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.

like image 187
Gab Avatar answered Sep 21 '22 21:09

Gab


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

like image 29
Mufaddal M Motiwala Avatar answered Sep 21 '22 21:09

Mufaddal M Motiwala