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

Splitting a td tag into multiple columns dynamically with Angular

I am attempting to dynamically split the table element into separate columns. My desired layout should resemble this: The values for name and surname are fixed at 1, but the values for subjects and grades can vary (there may be 2 or more columns). Below ...

When trying to upload a file with ng-upload in Angular, the error 'TypeError: Cannot read properties of undefined (reading 'memes')' is encountered

Struggling with an issue for quite some time now. Attempting to upload an image using ng-upload in angular, successfully saving the file in the database, but encountering a 'Cannot read properties of undefined' error once the upload queue is comp ...

When I delete the initial element from the array, the thumbnail image disappears

Using react-dropzone, I am attempting to implement image drag and drop functionality. The dropped image is stored in the React state within a files array. However, a problem arises when removing an image from the array causing the thumbnails of the remain ...

Obtaining the dimensions of each individual child component within an NgTemplate

I have the following code snippet within my template. While I can iterate through its components using `get`, it does not return an object that allows me to access deeper into the HTML attributes. <ng-template #container></ng-template> Compon ...

Angular2 Directive that Duplicates a Group of <tr> Elements

How can I build a component that generates HTML based on this data and HTML code? The <head> and <tbody> sections are projected, and I understand how to project multiple elements. However, I am unsure of how to repeat the projected <tr> i ...

I keep encountering an error with the Next.js Image component, even though I have properly included the src prop

By passing the src prop to the UserCard component, I am encountering the following error message. Error Unhandled Runtime Error Error: Image is missing required "src" property. Make sure you pass "src" in props to the `next/image` comp ...

Using Typescript to incorporate Next.js on Firebase Functions

I'm currently working on deploying a Next.js application to Firebase Functions. import next from 'next' import {https} from 'firebase-functions' const server = next({ dev: process.env.NODE_ENV !== 'production', conf: ...

What types of modifications do ViewChildren and ContentChildren QueryLists keep an eye out for?

Imagine you come across the following lines of code: https://i.stack.imgur.com/7IFx1.png And then, in a different section, you stumble upon this code block: https://i.stack.imgur.com/qac0F.png Under what circumstances would () => {} be executed? Wha ...

Steps for creating a TypeScript project with React Native

Hey there, I'm just starting out with react-native and I want to work on a project using VS Code. I'm familiar with initializing a project using the command "react-native init ProjectName", but it seems to generate files with a .js extension inst ...

Sending properties to MUI Box component enhancer (Typescript)

I'm having trouble figuring out how to pass props to override the Box component. I specifically need to pass position="end" as InputAdornment requires it, but I can't seem to find the proper way in the documentation. Here's the complete co ...

What are the benefits of using one state in React with useState compared to having multiple states?

Is it more beneficial to optimize and enhance code readability in React using Hooks and Functional components by utilizing a single setState hook or having multiple hooks per component? To further elaborate, let's consider the following: When workin ...

An API key was not included in your request. To proceed, please ensure your API key is included in the Authorization header

I encountered an error while using the checkout function with Stripe: API key not provided. Please include your API key in the Authorization header using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). Refer to https://stripe.com/doc ...

NextJS is throwing an error when trying to access the value returned

I've been exploring next.js and am trying to implement useContext for managing global state. However, I'm encountering difficulty in passing the value from provider to child element as the returned value of language in header.js is coming up as u ...

Issue with Mongoose failing to generate a new document in MongoDB

I am developing an education portal website using Next.js and MongoDB (mongoose). The school admin has the ability to add important notices from their dashboard, which can be viewed by students in their own dashboard. Due to the nature of most mongoose ope ...

Creating React Context Providers with Value props using Typescript

I'd prefer to sidestep the challenge of nesting numerous providers around my app component, leading to a hierarchy of provider components that resembles a sideways mountain. I aim to utilize composition for combining those providers. Typically, my pro ...

Creating a Typescript interface for a anonymous function being passed into a React component

I have been exploring the use of Typescript in conjunction with React functional components, particularly when utilizing a Bootstrap modal component. I encountered some confusion regarding how to properly define the Typescript interface for the component w ...

What could be causing AWS Elastic Beanstalk to fail in passing the custom JWT?

I have recently deployed a Next.js with Next-Auth project on AWS EB and everything seems to be in order. However, I am encountering an issue with the sign-in form. The browser console is showing the following error: 502 Bad Gateway: POST http://----.elas ...

The value stored within an object does not automatically refresh when using the useState hook

const increaseOffsetBy24 = () => { setHasMore(false); dispatch(contentList(paramsData)); setParamsData((prevState) => ({ ...prevState, offset: prevState.offset + 24, })); setHasMore(true); }; This function increment ...

Unable to pass extra parameters when using NextAuth

In my NextJS 13 app, I am utilizing NextAuth for authentication. Once the user is authenticated, the session initially returns name, email, and picture. My objective is to introduce an extra parameter called progress that will be updated as the user works. ...

What is the process for including a unique attribute for child elements within a React component using TypeScript?

I have a component that creates a Table of Contents (TOC) and List for the child elements. These children can be any JSX.Element. Here is an example... <SectionScrollList> <View key="first"/> <View key="second"/> ...