Contextual routing and modal routes in Next.js
Tags: javascript, react, nextjs
Published: 2020-10-18 20:24:49

**Contextual routing** is a widespread UI technic made popular by applications like Facebook, Instagram and Reddit usually in the shape of **Modal Routes**.

Modal routes pattern consists of opening a modal while temporary **replacing the current URL** (usually with the one pointing to the resource being displayed in-modal):

{% youtube 4NR2uOBsIu4 %}

## Basic contextual routing

[Next.js][next-js] router recently added support for contextual routing by simply providing `Link` component with the relevant `href` + `as` props.

```js
import Link from 'next/link';

<Link
  href="/post-list?postId=42"
  as="/post/42"
>
  <a>Open modal route</a>
</Link>
``` 
The above example would update browser address' path to `/post/42` while rendering the page specified as `href` (`/post-list` with `postId` parameters equal to `42`).

This leads to **2 possible outcomes**:

- user closes the modal and the URL is restored to the value where contextual routing was started (in our case `/post-list`)
- user refreshes the page landing on the actual page described by the URL

## HREF property

The minimal necessary information to render a Next.js page consists of:

- page `pathname` (eg. `/post/[id]`)
- page params (eg. `id=42`)

The main idea behind contextual routing consists of creating a **divergence** between the URL displayed and the page actually rendered.

Since contextual navigation replaces the URL, it means that rendered pages cannot rely anymore on the URL to retrieve `pathname` and relative page params.

**`href` prop** plays therefore the crucial role of supplying the aforementioned information as a **single string**:

```js
// page pathname + all required params as query string
const href = router.pathname + `?param1=1&param2=2`
```

## Real world scenario

Before starting contextual routing navigation you need to know **3 information beforehand**:

- `as` path (the path displayed during contextual routing)
- `href` path
- return `href` (the path to return to to end contextual routing)

**href** and **return href** are trivial to get when the starting page has a static path, let's say: `/post-list`.

Things gets trickier when starting page path is not statically defined, in other words when the path includes **dynamic parameters** like: `/post-list/bob` (where `bob` is an author name).

This means `href` has to be generated from initial page `pathname` plus route params and then **persisted** during the whole contextual routing navigation in order to keep the page alive.

On top of this a developer might want to **extend available route params** with extra ones to be made available during contextual navigation. Eg:

```js
const as = 'post/42'
const returnHref = 'post-list/bob'
const href = 'post-list/[author]?author=bob&id=42'
```
The example above provides `id=42` as extra param to make the modal route aware of which post should be displayed.

[This demo][github-example] shows a basic implementation of what you just read.

## The devil is in the details

There's is an extra pitfall worth mentioning: the application would loose reference to `returnHref` after a page reload followed by one or more back button presses.

This means the application won't be able to restore the initial URL while the modal route is open, breaking the user flow or forcing Next.js to reload the page.

## One hook to rule them all

In order to overcome this issue and make contextual routing setup trivial, I wrapped the necessary boilerplate logic in a React hook published as [`next-use-contextual-routing`][package-npm].

You can read the source code and star it [on github][package-github]. It's **fully tested** and weights **~0.5 kb gzipped**.

```js
import Link from 'next/link';
import { useContextualRouting } from 'next-use-contextual-routing';

//...
const { makeContextualHref, returnHref } = useContextualRouting();

<Link
  as="/post/42"
  href={makeContextualHref({ id: 42 })}
  shallow
>
  <a>Open modal route</a>
</Link>

```

The hook returns 2 values:

**`makeContextualHref`**: a function returning the `href` value necessary to start contextual navigation. It optionally accepts an object providing **extra `href` parameters**.

**`returnHref`**: the path to return to to close contextual navigation.

[The demo mentioned above][github-example] makes use of `next-use-contextual-routing` hook. Check it out to make sure it can suit your needs.

[next-js]: https://nextjs.org/
[next-docs-example]: https://github.com/vercel/next.js/tree/v9.5.5/examples/with-route-as-modal
[github-example]: https://toomuchdesign.github.io/next-use-contextual-routing/
[package-npm]: https://www.npmjs.com/package/next-use-contextual-routing
[package-github]: https://github.com/toomuchdesign/next-use-contextual-routing