The type "AppRouterInstance" cannot be assigned to type "nextRouter"

Within my Next.js project, a registration form is included as seen below:

"use client";

import * as React from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";

import { cn } from "@/lib/util";
import { userAuthSchema } from "@/lib/validations/auth";
import { buttonVariants } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Icons } from "@/components/icons";
import { useRouter } from "next/navigation";
import { registerUser } from "@/lib/register-user";

export type FormData = z.infer<typeof userAuthSchema>;

export function RegisterForm() {
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors },
  } = useForm<FormData>({ resolver: zodResolver(userAuthSchema) });

  const [isLoading, setIsLoading] = React.useState<boolean>(false);
  const [error, setError] = React.useState<string | null>(null);

  const router = useRouter();

  async function onSubmit(submittedData: FormData) {
    await registerUser({submittedData, setError, setIsLoading, reset, router})
  }


  return (
    <div>
      {error && <p className="mb-5 text-sm text-red-500 animate-in fade-in-0 slide-in-from-left-1">{error}</p>}
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="grid gap-6">
          <div className="grid gap-1">
            <Label htmlFor="email">Email</Label>
            <Input
              id="email"
              type="email"
              placeholder="<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b3ddd2ded6f3d6cbd2dec3dfd69dd0dcde">[email protected]</a>"
              autoCapitalize="none"
              autoComplete="email"
              autoCorrect="off"
              disabled={isLoading}
              {...register("email")}
            />
            {errors.email && (
              <p className="px-1 text-xs text-red-600 animate-in fade-in-0 slide-in-from-left-1">
                {errors.email.message}
              </p>
            )}
          </div>
          <div className="grid gap-1">
            <Label htmlFor="password">Password</Label>
            <Input
              id="password"
              type="password"
              autoCapitalize="none"
              autoComplete="off"
              autoCorrect="off"
              disabled={isLoading}
              {...register("password")}
            />
            {errors.password && (
              <p className="px-1 text-xs text-red-600 animate-in fade-in-0 slide-in-from-left-1">
                {errors.password.message}
              </p>
            )}
          </div>
          <button className={cn(buttonVariants())} disabled={isLoading}>
            {isLoading && (
              <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
            )}
            Register
          </button>
        </div>
      </form>
    </div>
  );
}

An observation to make is the passing of a router argument in the registerUser() function:

  async function onSubmit(submittedData: FormData) {
    await registerUser({submittedData, setError, setIsLoading, reset, router})
  }

Below you will find the implementation of the registerUser function:

import "client-only";
import type { FormData } from "@/components/auth/register-form";
import { NextRouter } from "next/router";

const RESPONSE_MESSAGES = {
  USER_DOCUMENT_CREATED: "New user document created",
  USER_DOCUMENT_UPDATED: "Existing user document updated",
  USER_ALREADY_REGISTERED: "User already registered",
  EMAIL_VERIFICATION_LINK_SENT: "Email verification link sent",
  EMAIL_VERIFICATION_LINK_ALREADY_SENT:
    "Email verification link already sent. Please check your inbox and verify your email.",
  REGISTRATION_FAILED: "Registration failed. Please try again.",
};

interface RegisterUserProps {
  submittedData: FormData;
  setError: React.Dispatch<React.SetStateAction<string | null>>;
  reset: () => void;
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
  router: NextRouter;
}

export async function registerUser({
  submittedData,
  setError,
  reset,
  setIsLoading,
  router,
}: RegisterUserProps) {
  setIsLoading(true);
  setError(null);

  try {
    // Attempt to register the user
    const registerResponse = await fetch("/api/register", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(submittedData),
    });

    // Registration failed
    if (!registerResponse.ok) {
      throw new Error(RESPONSE_MESSAGES.REGISTRATION_FAILED);
    }

    const registerResponseData = await registerResponse.json();

    // User already registered
    if (
      registerResponseData.message === RESPONSE_MESSAGES.USER_ALREADY_REGISTERED
    ) {
      setError(RESPONSE_MESSAGES.USER_ALREADY_REGISTERED);
      reset();
      return;
    }

    // Email verification link was already sent
    if (
      registerResponseData.message ===
      RESPONSE_MESSAGES.EMAIL_VERIFICATION_LINK_ALREADY_SENT
    ) {
      setError(RESPONSE_MESSAGES.EMAIL_VERIFICATION_LINK_ALREADY_SENT);
      reset();
      return;
    }

    // New user. Send email verification link
    if (
      registerResponseData.message ===
        RESPONSE_MESSAGES.USER_DOCUMENT_CREATED ||
      registerResponseData.message === RESPONSE_MESSAGES.USER_DOCUMENT_UPDATED
    ) {
      const { email, emailVerificationToken } = registerResponseData;

      const emailResponse = await fetch(
        "/api/send-registration-email-verification-link",
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ email, emailVerificationToken }),
        },
      );

      // Check if email was successfully sent
      if (!emailResponse.ok) {
        throw new Error(RESPONSE_MESSAGES.REGISTRATION_FAILED);
      }

      const emailResponseData = await emailResponse.json();

      if (
        emailResponseData.message ===
        RESPONSE_MESSAGES.EMAIL_VERIFICATION_LINK_SENT
      ) {
        reset();
        router.replace("/register/verify-email");
      }
    }
  } catch (error) {
    setError((error as Error).message);
  } finally {
    setIsLoading(false);
  }
}

The utilization of TypeScript's type annotation for router, paired with NextRouter can be observed:

interface RegisterUserProps {
  submittedData: FormData;
  setError: React.Dispatch<React.SetStateAction<string | null>>;
  reset: () => void;
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
  router: NextRouter;
}

Nonetheless, an error arises where a red squiggly line appears under router within the RegisterForm component.

https://i.stack.imgur.com/6ZFD1.png

The error message indicates the following:

Type 'AppRouterInstance' is not assignable to type 'NextRouter'.
  Type 'AppRouterInstance' lacks properties such as route, pathname, query, asPath, among others.ts(2322)
register-user.ts(20, 3): The expected type originates from property 'router', declared here on type 'RegisterUserProps'
(property) RegisterUserProps.router: NextRouter

Could someone provide insight into this issue? How can this TypeScript error be resolved?

Answer №1

In Next.js, there are two options for using the useRouter hook. The old version can be found in 'next/router', while the new version (introduced in v13) is imported from 'next/navigation'.

The type for the old useRouter is NextRouter, whereas the type for the new one is AppRouterInstance.

If you accidentally mix up the types when using these hooks in your component and function, a helpful tip is to let TypeScript infer the type of the function by using: UseRouter: typeof useRouter. You can also get the return type by using the ReturnType helper like this:

type router = ReturnType<typeof useRouter>
.

Update

Although the Next.js team doesn't directly export the type for useRouter due to lack of necessity, you can still access it by importing the internal type like so:

import { AppRouterInstance } from "next/dist/shared/lib/app-router-context"

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to assign table rows to various interfaces in typescript?

Assuming I have the interfaces provided below: export interface IUserRow { id: string, state: string, email: string, } export interface ITableRow { id: string, [key: string]: any; } export type Rows = ITableRow | IUserRow; // additio ...

The construction was unsuccessful due to errors in the webpack process

I encountered a sudden error in my Next.js app. Is there any solution available to resolve this issue? ./pages/_app.tsx Error: [BABEL] C:\Projects\skribeNew\app-web\pages\_app.tsx: You provided us with a visitor for the node type T ...

Tips for importing several makeStyles in tss-react

When upgrading from MUI4 to MUI5 using tss-react, we encountered a problem with multiple styles imports in some files. const { classes } = GridStyles(); const { classes } = IntakeTableStyles(); const { classes } = CommonThemeStyles(); This resulted in ...

When utilizing RxJS, the process of filtering Observable data may not function as expected if the filtering is carried out within a separate function rather than directly within the subscribe

While attempting to filter data from an external source using the RxJS filter on Observables, I encountered an issue where all records were returned instead of just the ones meeting the filtering criteria. This problem occurred when the code was within a l ...

What steps are required to utilize NgbSortableHeader for sorting a bootstrap table through programming?

I have a bootstrap HTML table (operated by ng-bootstrap for Angular and utilized NgbdSortableHeader to arrange table columns by clicking on the column). When I click on an element, it sorts the column in ascending, descending, or ''(none) order. ...

What is the best way to specify the data type of a value within a map in TypeScript?

I need assistance defining the value of a key in a map as a key-value pair in TypeScript. map: { key: someStruct } Is it possible to declare the type of someStruct and initialize it simultaneously? What is the best approach for accomplishing this? ...

successfully fetched using useSWR, but the returned data is undefined

Currently, I am utilizing the useSWR hook to retrieve and exhibit data from an API. The code snippet is as follows: const backendURL = process.env.NEXT_PUBLIC_BACKEND_URL; const { data: session, status } = useSession(); let userToken = null; useEff ...

Is it necessary to include async/await in a method if there is already an await keyword where it is invoked?

Here are the two methods I have written in Typescript: async getCertURL(pol: string): Promise<string> { return await Api.getData(this.apiUrl + pol + this.certEndpoint, {timeout: 60000}).then( (response) => { return response.data.certUR ...

Having trouble implementing font css file in Reactjs

When working with Reactjs (Nextjs), every time I try to incorporate "css" into my project, I encounter the following error on my screen: Module not found: Can't resolve '../fonts/fontawesome-webfont.eot?v=4.7.0' How can I solve this issue? ...

Why is my Next.js <Image> tag displaying JPG instead of Webp or Avif formats?

When using Nextjs, I anticipated seeing the Image component rendered as .WebP or .AVIF format, but it continues to display jpg instead - Here is my next.config.js file: /** @type {import('next').NextConfig} */ module.exports = { images: { ...

Learn how to easily set a background image using Tailwind CSS in a Next.js application

Trying to insert a background image into a section, but encountering an unexpected error. Here is the code I am using: The error message says: What steps should I take to resolve this issue? Even after removing the code, the error persists. ...

Execute a specialized function with imported modules and specified parameters

Within an npm project, I am looking to execute a custom function with arguments, or ideally provide it as a script in the package.json file like this: npm run custom-function "Hello, World". Currently, I have a file called src/myFunction.ts: import * as e ...

Enhancing TypeScript Native Interface Properties in TypeScript 2.4

After discovering an error in the native Typescript interface for HTMLTextAreaElement, I realized the need to make a correction. The current interface looks like this: interface HTMLTextAreaElement { setSelectionRange(start: number, end: number): void; ...

Having trouble filtering results in Apollo Client cache, attempted to use readQuery but receiving a null response

Exploring the functionality of the Apollo Client cache has been a priority for me lately. My goal is to avoid unnecessary server calls and improve paging efficiency. The technologies in play here are Next.js, Apollo Client on the front-end, and Keystone.js ...

Utilizing a component from a different module

Currently working on Angular 4 and facing an issue with referencing a component from another module. In my EngagementModule, the setup is defined as below: import { NgModule } from '@angular/core'; // other imports... @NgModule({ imports: [ ...

What could be causing the excessive blank space below the image in my React application? What is generating this enigmatic div element?

I've been working on creating a login Component in React, and I'm facing an issue when trying to add the brand image. There is a significant amount of white space appearing below the image. After some investigation, I found that there is a myste ...

Exploring VSCode Debugger with Typescript: Traversing through Step Over/Into leads to JavaScript file路径

Just starting out with VSCode and using it to debug node.js code written in Typescript. One thing that's been bothering me is that when I stop at a breakpoint and try to "Step Over" or "Step Into", the debugger takes me to the compiled Javascript file ...

Listening for a long press in Angular 2: A step-by-step guide

Check out this link for an AngularJS example. Curious how this functionality translates to Angular 2? ...

Deciding when to utilize an interface, when to avoid it, and when to opt for a constructor in Typescript for the creation of JavaScript arrays

Exploring the concept of JavaScript object arrays in TypeScript In my current project, I am retrieving a JSON array from an observable. It seems that I can define and initialize the array without necessarily specifying an interface or type. let cityList[ ...

Error message in NextJS 13: The response body object must not be altered or restricted

I encountered a perplexing error in my NextJS 13 server (app router) that reads TypeError: Response body object should not be disturbed or locked. The issue arises sporadically when attempting to register a new user, but not consistently. ⨯ TypeError: R ...