# Recipe: links with next/link and the Next.js App Router SDK

> Render Uniform link parameters with next/link: get the href, tell internal links from external links, and keep the custom link attributes that authors set.

Source: https://docs.uniform.app/docs/sdk/nextjs-app-router/recipes/links

**Goal:** render a link that an author sets in a link parameter. Use `next/link` for links in your site, and a plain `<a>` element for other links.

## Prerequisites

- A Next.js app with the SDK. Refer to [Next.js App Router SDK](https://docs.uniform.app/docs/sdk/nextjs-app-router).
- A component definition with a parameter of the type **Link**, for example `link`.
- The `@uniformdev/canvas` and `@uniformdev/richtext` packages, at the same version as the SDK:

  ```bash
  v=20.81.1-alpha.25.sha-f076f9b857
  npm install @uniformdev/canvas@$v @uniformdev/richtext@$v
  ```

## The value of a link parameter

The type of a link parameter value is `LinkParamValue` from `@uniformdev/canvas`:

```ts
type LinkParamValue =
  | {
      type: "projectMapNode"; // a page of the project map
      projectMapId: string;
      nodeId: string;
      path: string;
      dynamicInputValues?: Record<string, string>;
      attributes?: Record<string, string>;
    }
  | {
      type: "url" | "tel" | "email";
      path: string;
      attributes?: Record<string, string>;
    }
  | undefined;
```

`attributes` contains the [custom link attributes](https://docs.uniform.app/docs/guides/models/components/parameters#link) that the author set, for example `target` or `title`.

`@uniformdev/richtext` has three helpers for this value:

| Helper | Returns |
| --- | --- |
| `linkParamValueToHref(link)` | The `href` as `string \| undefined`. It adds `mailto:` to email links and `tel:` to phone links. |
| `linkParamValueToHtmlAttributes(link)` | `href` and the custom link attributes, as HTML attributes. |
| `linkParamValueToAnchorProps(link)` | The same as `linkParamValueToHtmlAttributes`, but for React: it changes `class` to `className`. |

## Step 1: Render a simple link

If you need only the `href`, use `linkParamValueToHref`:

`components/button.tsx`

```tsx
import type { LinkParamValue } from "@uniformdev/canvas";
import type { ComponentParameter, ComponentProps } from "@uniformdev/next-app-router/component";
import { linkParamValueToHref } from "@uniformdev/richtext";
import Link from "next/link";

type ButtonParameters = {
  label?: ComponentParameter<string>;
  link?: ComponentParameter<LinkParamValue>;
};

export const Button = ({ parameters: { label, link } }: ComponentProps<ButtonParameters>) => {
  const href = linkParamValueToHref(link?.value);

  if (!href) return null;

  return (
    <Link href={href} className="rounded bg-black px-4 py-2 text-white">
      {label?.value ?? "Read more"}
    </Link>
  );
};
```

This example ignores the custom link attributes. Use step 2 to keep them.

## Step 2: Make a link component for all link types

Make a component that uses `next/link` for internal links and `<a>` for other links. It also adds the custom link attributes:

`components/smart-link.tsx`

```tsx
import type { LinkParamValue } from "@uniformdev/canvas";
import { linkParamValueToAnchorProps } from "@uniformdev/richtext";
import Link from "next/link";
import type { ReactNode } from "react";

type SmartLinkProps = {
  link: LinkParamValue;
  className?: string;
  children: ReactNode;
};

// True for a path in this site, for example "/about", but not for "//cdn.example.com".
const isInternalPath = (href: string) => href.startsWith("/") && !href.startsWith("//");

export const SmartLink = ({ link, className, children }: SmartLinkProps) => {
  // href, plus the custom link attributes that the author set (target, rel, title, and so on).
  const { href, className: authorClassName, ...attributes } = linkParamValueToAnchorProps(link);
  const classes = [className, authorClassName].filter(Boolean).join(" ") || undefined;

  // No link, or a link that the helper removed for security: render the text only.
  if (!href) return <span className={classes}>{children}</span>;

  // Project map links and site paths: client-side navigation with next/link.
  if (link?.type === "projectMapNode" || (link?.type === "url" && isInternalPath(href))) {
    return (
      <Link {...attributes} href={href} className={classes}>
        {children}
      </Link>
    );
  }

  // External URLs, email (mailto:) and phone (tel:) links: a plain anchor.
  return (
    <a {...attributes} href={href} className={classes}>
      {children}
    </a>
  );
};
```

## Step 3: Use the link component in a Uniform component

`components/navigation-link.tsx`

```tsx
import type { LinkParamValue } from "@uniformdev/canvas";
import {
  type ComponentParameter,
  type ComponentProps,
  UniformText,
} from "@uniformdev/next-app-router/component";
import { SmartLink } from "./smart-link";

type NavigationLinkParameters = {
  label?: ComponentParameter<string>;
  link?: ComponentParameter<LinkParamValue>;
};

export const NavigationLink = ({
  parameters: { label, link },
  component,
}: ComponentProps<NavigationLinkParameters>) => (
  <SmartLink link={link?.value} className="hover:underline">
    {label ? <UniformText component={component} parameter={label} placeholder="Link text" /> : null}
  </SmartLink>
);
```

Register `NavigationLink` in `components/resolveComponent.tsx`. You can use it in the `navigation` slot of a [global header](https://docs.uniform.app/docs/sdk/nextjs-app-router/recipes/global-header-footer).

## Notes

- **Internal and external links:** a `projectMapNode` link always goes to a page of your site. A `url` link can go to your site or to a different site. The example uses `next/link` only for a `url` link that starts with `/`. `next/link` prefetches the page and changes pages without a full reload. For other links, a plain `<a>` element is better.
- **Security:** the helpers remove event handler attributes (`on…`), the attributes `href`, `src`, `style` and `formaction`, and values with the `javascript:`, `data:` or `vbscript:` scheme. If the `path` has one of these schemes, the `href` is `undefined`.
- **`target="_blank"`:** if the author sets `target="_blank"` and no `rel`, the helpers add `rel="noopener noreferrer"`.
- **Class names:** an author can set a `class` attribute. The example adds it to the class names of the component.
- **Links in rich text:** `UniformRichText` renders links as plain `<a>` elements with the custom link attributes. You do not have to do the steps on this page for rich text. Refer to [Components and slots in code](https://docs.uniform.app/docs/sdk/nextjs-app-router/components#uniform-rich-text).
