13

When a site has a dialog, it's often done purely in JS, with no "pages" (ie. routing) involved. However, sometimes you want your dialog to have it's own URL, so that if the user refreshes the page, they come back to the page with the dialog up.

In Next.js I've managed most of the above. I have a base page route, and then a second route which renders that page component, plus my dialog component. When I click the link it changes the URL, and then I see my dialog ... and if I refresh, I still see the page/dialog.

However, when I first click the link, it re-renders the page, losing other stateful changes. I thought that using the shallow prop on my link would prevent this, but it didn't (I guess because the dialog routes were different routes, and not just query parameters?). I also thought perhaps using as instead of href would work ... but it turns out href is required.

So, to summarize, is it possible in Next.js to create a link which:

  1. does change the URL (to an actual page, not just the query parameters)
  2. does render a new (dialog) component
  3. doesn't re-render the main page component

P.S. I'm porting a site from Gatsby, where this is possible, and while I could change my routes to use query parameters instead of real routes, that would effectively "delete" a bunch of pages on my site ... with SEO consequences :(

1 Answer 1

14

Here's the pattern I figured out to make this work.

Step #1: Make Your Main Page (With Optional Dialog)

Make your (for instance, pages/todo/[pageId].js, for a To Do app) page. Its default export should be a component that uses the Next router to decide whether the dialog is shown.

For instance, if your modals were going to add /modal/5 to the URL (to show modal #5) you could do:

data => { const router = useRouter(); const { modal } = router.query; return ( <> <Page {...data} /> {modal ? (<Dialog {...data} />) : null} </> ); 

(NOTE: By using React "portals" you can still render the dialog directly under the <body> element, if you want.)

Step #1.5: Make the Pages Dynamic Export Functions

Meanwhile, make a getStaticProps export as normal. Also make a getStaticPaths, but in it you want to return two path objects for every page: one with your dialog's parameter, and one with that parameter set to an empty string. This tells Next.js that the page can have those two URL states of open/closed.

For instance, if your pages were /todo/1 and you had a link for a "modal" parameter for modal #3, you'd have a "modal URL" of /todo/1/modal/3. You'd need to return a path for that and one for /todo/1.

Step #2: Make Your Link

This part is a little tricky; you want your link to look like this:

<Link as={`/todo/${pageId}/modal/${modalId}`} href={{ pathname: `/todo/[pageId]?modalId=[modalId]`, query: { pageId, modalId }, }} scroll={false} shallow={true} > Click me to open dialog </Link> 

In other words, the as prop will be your modal URL, and Next will change the location bar to that URL when the link is clicked. But it will actually use the href object, which is for the non-modal URL with query parameters for the page/modal IDs. It has to use query parameters in order to keep the core URL the same.

Finally we set scroll to false to that the link doesn't scroll to the top of the page, and shallow to true so as to avoid refetching pointless data. With all that done, everything will work ... unless you refresh the page with the dialog open.

This is because as only fakes out the URL, and we haven't actually made a route for it

Step #3: Make The Modal "Page"

Create a page file at the proper directory structure (eg. /todo/[pageId]/modal/[modalId].js) ... but this file will be super-simple. In fact, you can even re-use your component and getStaticProps from your main page!

export { default, getStaticProps, } from 'pages/todo/[pageId]'; 

You will need to make a new version of getStaticPaths though. It will be identical to the main page one, except that it will only return one path object per path (the one with the modal parameter in the ID).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.