# LinkProvider

A provider that sends each link of ferry-ui through the link component of your router.

```tsx
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
  LinkProvider,
  toast,
  type LinkComponent,
} from 'ferry-ui'

// In a real app, the adapter renders the link of the router: <Link to={href} {...props} />.
// This adapter stays on the page and shows the target of the link.
const DemoLink: LinkComponent = ({ href, onClick, ...props }) => (
  <a
    href={href}
    {...props}
    onClick={(event) => {
      onClick?.(event)
      event.preventDefault()
      toast('The link component got a click', { description: `Target: ${href}` })
    }}
  />
)

export default function LinkProviderHero() {
  return (
    // A real app mounts the provider one time, near its root.
    <LinkProvider component={DemoLink}>
      <Breadcrumb>
        <BreadcrumbList>
          <BreadcrumbItem>
            <BreadcrumbLink href="#customers">Customers</BreadcrumbLink>
          </BreadcrumbItem>
          <BreadcrumbSeparator />
          <BreadcrumbItem>
            <BreadcrumbLink href="#acme">Acme</BreadcrumbLink>
          </BreadcrumbItem>
          <BreadcrumbSeparator />
          <BreadcrumbItem>
            <BreadcrumbPage>INV-2041</BreadcrumbPage>
          </BreadcrumbItem>
        </BreadcrumbList>
      </Breadcrumb>
    </LinkProvider>
  )
}
```

## Usage

ferry-ui does not import a router. A component with a link takes an `href` string and renders an `<a>` element. `LinkProvider` replaces this element with the link of your router. Then a click does not load a full page.

Write an adapter at module level. Mount the provider one time, near the root of the app.

```tsx title="providers.tsx"

// Module level: the component keeps the same identity between renders.
const RouterLink: LinkComponent = ({ href, ...props }) => <Link to={href} {...props} />

  return <LinkProvider component={RouterLink}>{children}</LinkProvider>
}
```

- **Forward all the props.** The adapter gets `href` and the attributes of an anchor, for example `className`, `onClick` and `aria-current`. Give them all to the link.
- **Keep the adapter stable.** Define it at module level, not in a component.
- **Not for your own links.** For a link that you write, use the link of your router.
- **Not necessary with no router.** With no provider, ferry-ui renders plain `<a>` elements.

## Examples

### Adapters

The adapter gives `href` to the link of your router. ferry-ui gives a plain string. If your router has typed routes, cast `href` in the adapter.

| Router | Adapter |
| --- | --- |
| React Router | `({ href, ...props }) => <Link to={href} {...props} />` |
| Next.js | `({ href, ...props }) => <NextLink href={href} {...props} />` |
| TanStack Router | `({ href, ...props }) => <Link to={href} {...props} />` |

The [Routing](/docs/handbook/routing) page shows the full setup.

### Components with a link

These components render their links through the provider. Each one also has a `linkComponent` prop.

| Page | Components |
| --- | --- |
| [Breadcrumb](/docs/components/breadcrumb) | `BreadcrumbLink` |
| [Page](/docs/components/page) | `PageBackLink` |
| [Resource Card](/docs/components/resource-card) | `ResourceCard` |
| [Description List](/docs/components/description-list) | `DescriptionItem` |
| [Top Bar](/docs/components/top-bar) | `TopBarLogo`, `TopBarSegment`, `TopBarIconButton` |
| [Icon Rail](/docs/components/icon-rail) | `IconRail`, `IconRailItem` |
| [Mobile Nav](/docs/components/mobile-nav) | `MobileNav` |
| [Inner Menu](/docs/components/inner-menu) | `InnerMenu` |
| [Command Menu](/docs/components/command-menu) | `CommandMenu` |
| [Resource Switcher](/docs/components/resource-switcher) | `ResourceSwitcher` |

A navigation item with `external` does not use the link component. It is a plain `<a>` element that opens a new tab.

### Another link for one component

The `linkComponent` prop of a component overrides the provider for this component.

```tsx
const PlainLink: LinkComponent = (props) => <a {...props} />

<PageBackLink href="/customers" linkComponent={PlainLink}>
  Customers
</PageBackLink>
```

### A link in your component

Does your component render a link? Call `useLinkComponent()` to get the link component. Give it the `linkComponent` prop of your component.

```tsx title="docs-link.tsx"

interface DocsLinkProps {
  href: string
  children: ReactNode
  className?: string
  /** Overrides the `LinkProvider` of the app for this link. */
  linkComponent?: LinkComponent
}

  const Link = useLinkComponent(linkComponent)
  return (
    <Link href={href} className={cn('text-primary underline-offset-4 hover:underline', className)}>
      {children}
    </Link>
  )
}
```

## API reference

### LinkProvider

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `component` (required) | `LinkComponent` |  | The link to render for every ferry-ui link below (a router adapter). Must forward all props to an anchor. Define it at module level so it keeps the same identity between renders. |
| `children` | `ReactNode` |  | The part of the app whose ferry-ui links go through `component` (usually the whole app). |

### useLinkComponent

`useLinkComponent(override)` returns a `LinkComponent`. If you give `override`, the hook returns it. If not, the hook returns the component of the nearest provider, or a plain `<a>`.

| Parameter | Type | Role |
| --- | --- | --- |
| `override` | `LinkComponent`, optional | The `linkComponent` prop of your component. |

### Types

| Type | Definition |
| --- | --- |
| `LinkComponent` | `React.ComponentType<LinkComponentProps>` |
| `LinkComponentProps` | The attributes of the `<a>` element, with a required `href` string. |
| `LinkProviderProps` | The props of `LinkProvider`. |
