Next.js Discord

Discord Forum

NextJS 13's new Link, Typescript, and forwardRef

Answered
Mudi posted this in #help-forum
Open in Discord
MudiOP
I'm upgrading from Next 12 to 13. I have a component which wraps next/link, and renders either a regular <a> tag or a next/link component, depending on whether it's an external or internal link.

After the upgrade I'm getting a new TS error and I'm not sure how to deal with it.

 import { forwardRef } from "react";
import NextLink from "next/link";

import { getLinkInfo, isLinkInfo } from "lib/link";
import type { EntryInfo, LinkInfo } from "lib/link";

/**
 * A link which will render either an SPA link (`next/link`),
 * or a regular anchor tag, as appropriate.
 *
 * This often imported as `A` rather than `Link`.
 */
export const Link = forwardRef<
  HTMLAnchorElement,
  {
    /**
     * Either a string href, an EntryInfo structure, or a LinkInfo structure.
     */
    href?: string | EntryInfo | LinkInfo;
  } & Omit<React.ComponentProps<"a">, "href">
>(function LinkWithRef({ href, ...props }, ref) {
  if (href == null) return <a {...props} />;

  let linkInfo: LinkInfo | null = null;
  if (isLinkInfo(href)) linkInfo = href;
  else linkInfo = getLinkInfo(href);

  if (linkInfo.isLocal) {
    return (
      <NextLink ref={ref} href={linkInfo.href} {...props} />
    );
  }

  return <a ref={ref} href={linkInfo.href} {...props} />;
});


I'm getting the error on the ref prop where I return <NextLink ...:

Type 'string | ((instance: HTMLAnchorElement | null) => void) | RefObject<HTMLAnchorElement> | null' is not assignable to type 'Ref<HTMLAnchorElement> | undefined'.
  Type 'string' is not assignable to type 'Ref<HTMLAnchorElement> | undefined'.


I am guessing the type of the ref (currently HTMLAnchorElement) needs to change to accomodate Next 13's changes, but I don't know what it needs to change to. Or maybe I need to narrow a type somewhere?

I tried changing it to HTMLAnchorElement | typeof NextLink and that leads to a much more cryptic error.

Any ideas?
Answered by Mudi
Figured it out, the & Omit<React.ComponentProps<"a">, "href"> part of my prop types was including HTMLAnchorElement's own ref prop, and that was conflicting with the forwarded one. I needed to change the omission to "href" | "ref".
View full answer

7 Replies

MudiOP
I'm not even sure where the string it's complaining about is coming from. ref is of type ForwardedRef<HTMLAnchorElement>, which is ((instance: HTMLAnchorElement | null) => void) | { current: HTMLAnchorElement | null } | null and I don't see strings anywhere in there
MudiOP
Figured it out, the & Omit<React.ComponentProps<"a">, "href"> part of my prop types was including HTMLAnchorElement's own ref prop, and that was conflicting with the forwarded one. I needed to change the omission to "href" | "ref".
Answer
@Mudi I'm late but you can also type it as React.ComponentPropsWithoutRef<"a">, but since you are already using the Omit for href it makes sense to just include ref there
Oriental
the whole ref forwardref thing. WHY?!
Syntax of React and Next.js is somewhat really annoying
Please point me to good tutorials about this topic