DEV Community

Cover image for Next 13 Migration Case Study: useRouter vs useRouter
Ashish Kankal
Ashish Kankal

Posted on

Next 13 Migration Case Study: useRouter vs useRouter

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) 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:

import { useRouter } from 'next/router'
Enter fullscreen mode Exit fullscreen mode

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

import { useRouter } from 'next/navigation'
Enter fullscreen mode Exit fullscreen mode

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.

// 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;
Enter fullscreen mode Exit fullscreen mode

Creating a custom useRouter hook

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

//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;
Enter fullscreen mode Exit fullscreen mode

Final Step

Wrap the children in the pages and app directory with NextAppRouterContext.Provider:

Page Router

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

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

App Router

/// 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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)