Terry Windwalker
Terry Windwalker

Reputation: 1886

How to prevent '#' be converted to '%23' in Next/Link?

I was trying to use #{id} to move user to an element with the given id, but the href somehow always get its hashtag converted into %23. Is there a way to prevent this?

UPDATE: This is one of the components that have that issue. When the item.href contains "#", it would become %23 on rendering. It was used in a drop down menu component.

export interface NavItemType {
  id: string;
  name: string;
  href: string | "#" | "/#";
  toId?: string;
  targetBlank?: boolean;
  children?: NavItemType[];
  megaMenu?: MegamenuItem[];
  type?: "dropdown" | "megaMenu" | "none";
}
  const renderDropdownMenuNavlink = (item: NavItemType) => {
    return (
      <Link
        href={{
          pathname: item.href || undefined,
        }}
        passHref
      >
        <a
          target={item.targetBlank ? "_blank" : undefined}
          rel="noopener noreferrer"
          className={
            "flex items-center font-normal text-neutral-6000 dark:text-neutral-400 py-2 px-4 rounded-md hover:text-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 dark:hover:text-neutral-200" +
            (router.pathname === item.href ? "!font-medium !text-neutral-900 dark:!text-neutral-100" : "")
          }
        >
          {item.name}
          {item.type && (
            <ChevronDownIcon
              className="ml-2 h-4 w-4 text-neutral-500"
              aria-hidden="true"
            />
          )}
        </a>
      </Link>
    );
  };

Upvotes: 4

Views: 2005

Answers (2)

As @Marlom said, your url is encoded when using #, resulting with a %23 in your url. My solution is to use the hash property of the next/link component. Maybe parsing it before if your href contains the #? My solution would be :

<Link href={{ pathname: props.href, hash: props.hash }}>
</Link>

With your example (and the parsing) :

  const renderDropdownMenuNavlink = (item: NavItemType) => {
    const [pathname, hash] = item.href.split("#");
    return (
      <Link
        href={{
          pathname: pathname || undefined,
          hash: hash,
        }}
      >
        <a
          target={item.targetBlank ? "_blank" : undefined}
          rel="noopener noreferrer"
          className={
            "flex items-center font-normal text-neutral-6000 dark:text-neutral-400 py-2 px-4 rounded-md hover:text-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 dark:hover:text-neutral-200" +
            (router.pathname === item.href ? "!font-medium !text-neutral-900 dark:!text-neutral-100" : "")
          }
        >
          {item.name}
          {item.type && (
            <ChevronDownIcon
              className="ml-2 h-4 w-4 text-neutral-500"
              aria-hidden="true"
            />
          )}
        </a>
      </Link>
    );
  };

Or, if it is possible, adding a hash property to your NavItemType interface so you don't have to parse it :

export interface NavItemType {
  id: string;
  name: string;
  href: string;
  hash: string;
  toId?: string;
  targetBlank?: boolean;
  children?: NavItemType[];
  megaMenu?: MegamenuItem[];
  type?: "dropdown" | "megaMenu" | "none";
}

Upvotes: 0

Marlom
Marlom

Reputation: 688

NextJS uses the native UrlObject from node or browser if not SSR. pahname is specifically used for path name so anything not accepted there will be encoded, what you might need is hash but you might also be able to use your item.href directly into href prop of your Link

More info here

Upvotes: 4

Related Questions