Next 13 Migration Case Study: useRouter vs useRouter
Tags: webdev, nextjs, javascript, react
Published: 2023-06-18 11:15:57

In this article, we will learn how to share components between the **Pages** and the **App** Router which uses useRouter hook.

**Next 13** has released the new App router. 

The Next.js 13 App Router offers several advantages over the previous Pages Router, including:

- **Nested layouts**: The App Router allows you to define layouts that can be nested within each other, making it easier to create complex and reusable UIs.

- **Server components**: The App Router allows you to fetch data directly in server components, which can improve performance and SEO.

- **Streaming**: The App Router supports streaming of data, which can improve the user experience for applications that require real-time updates.

- **Error handling**: The App Router provides a more robust error handling mechanism than the Pages Router.
Parallel routes: The App Router allows you to define parallel routes, which can improve performance for applications that have multiple paths that can be navigated to at the same time.

And many more...

## Migrating to App Router
Migrating to App Router is not a one-step process. It has few steps involved ([mentioned here](https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration)) and might be complex depending on the application code.


## useRouter vs useRouter
Looks same!
The useRouter hook is used in the components used inside pages directory as:

```js
import { useRouter } from 'next/router'
```

However, in the new App Router, the `useRouter` hook needs to be imported from `next/navigation`

```js
import { useRouter } from 'next/navigation'
```

This makes it difficult to share components between pages and app directory.

## How to share Components between App router and Pages router?

Its still possible to share components between pages and app directory. This can be achieved by creating a custom hook and the context.

### Creating a context
We will need to create a context which will store the information whether the page is inside pages directory or app directory.

```js
// lib/next-router-context
"use client";

import { createContext } from "react";

export const NextRouterContext = createContext({});

const NextRouterContextProvider = ({ children, value }: any) => (
  <NextRouterContext.Provider value={value}>
    {children}
  </NextRouterContext.Provider>
);

export default NextRouterContextProvider;
```

### Creating a custom `useRouter` hook
The custom useRouter hook will export the `usePageRouter` or `useAppRouter` based on the context set in `NextAppRouterContext`:

```js
//hooks/useRouter.ts
"use client";
import { useContext } from "react";
import { NextAppRouterContext } from "./NextAppRouterContext";
import { useRouter as usePageRouter } from "next/router";
import { useRouter as useAppRouter } from "next/navigation";

const useRouter = () => {
  const context = useContext(NextAppRouterContext);
  const router = context === "app" ? useAppRouter : usePageRouter;
  return { ...router(), context };
};

export default useRouter;
```

### Final Step
Wrap the children in the pages and app directory with `NextAppRouterContext.Provider`:

**Page Router**

```js
// pages/_app.js
import NextRouterContextProvider from "@/lib/next-router-context";

 export default function MyApp({ Component, pageProps }) {
  return (
    <NextRouterContextProvider value="page">
      <Component {...pageProps} />
    </NextRouterContextProvider>
  )
}
```

**App Router**
```js
/// app/layout.tsx
import NextAppRouterContextProvider from '@/lib/next-router-context'
import '@/styles/globals.css'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <NextAppRouterContextProvider value="app">
        {children}
        </NextAppRouterContextProvider>
        </body>
    </html>
  )
}
```

And Thats it! Now you can share the components between the pages and app router. Just import the custom `useRouter` defined as above. 

Checkout the complete example [here](https://github.com/ashishkankal/next-use-router-example)