Custom Links and Link Options
Purpose and Scope
Custom links solve a common application problem: teams want one navigation component that carries product styling, accessibility conventions, preload behavior, analytics hooks, or third-party design-system behavior, while still keeping TanStack Router's route-aware type safety. The Router docs present this as a cross-cutting concern rather than a replacement routing model. Instead of sprinkling repeated link styling and navigation defaults throughout an app, you wrap an anchor-like component once and let Router continue to validate destinations, params, search, hash, and state through the same type parameters used by the built-in Link component.
Sources: docs/router/guide/custom-link.md, docs/router/api/router/useLinkPropsHook.md
Reusable link options solve a related but different problem. An object literal that is later spread into a Link can lose useful literal inference, so a route path can widen to a plain string before Router has a chance to validate it. The docs call out that this can delay type errors until the object is actually used. The linkOptions helper checks the object at definition time and returns the inferred input unchanged, which makes shared navigation definitions safer for menus, redirects, and imperative navigation calls.
Sources: docs/router/guide/link-options.md, docs/router/api/router/linkOptions.md
Relevant Source Files
- docs/router/guide/custom-link.md - Reader-facing guide for creating custom Link components with createLink, including React and Solid examples and third-party component integration notes.
- docs/router/guide/link-options.md - Guide for extracting Link, navigate, and redirect options into reusable constants or arrays while preserving eager type checking and inference.
- docs/router/api/router/linkOptions.md - API reference for linkOptions, including its accepted props contract and return behavior.
- docs/router/api/router/useLinkPropsHook.md - API reference for useLinkProps, which returns anchor props that can be applied directly to an anchor element for navigation.
Core Primitives
There are three primitives to distinguish before building abstractions. The first is the regular Router Link component, which remains the baseline component for declarative navigation. The second is createLink, a factory that accepts an anchor-like component and creates a Router-aware custom Link with the same type parameters as the built-in Link. The third is linkOptions, a helper for defining reusable navigation option objects or arrays before they are passed to Link, navigate, or redirect. These primitives are complementary: createLink shapes the component, while linkOptions shapes the data passed into navigation APIs.
Sources: docs/router/guide/custom-link.md, docs/router/guide/link-options.md
useLinkProps is lower level than createLink. The API reference describes it as a hook that takes an options object and returns React anchor attributes. Those returned props can be applied to an anchor element to navigate to a new location, including pathname, search params, hash, and location state changes. Use it when you are building a custom component directly around an anchor and need generated props rather than a component factory. In contrast, createLink is the ergonomic option when you already have a component that should behave like Router's Link.
Sources: docs/router/api/router/useLinkPropsHook.md, docs/router/guide/custom-link.md
Building a Custom Link
A basic custom link starts with an anchor-compatible component owned by your app. In the React guide, that component is implemented with forwardRef and regular anchor attributes, then passed to createLink. The exported CustomLink is typed with LinkComponent and renders the created component while adding a default preload value of intent. The important design choice is that the styling and defaults live in the wrapper, but callers still pass route-aware props such as to and params. That keeps component reuse from weakening route validation.
Sources: docs/router/guide/custom-link.md
import * as React from 'react'
import { createLink, LinkComponent } from '@tanstack/react-router'
interface BasicLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {}
const BasicLinkComponent = React.forwardRef<HTMLAnchorElement, BasicLinkProps>(
(props, ref) => {
return <a ref={ref} {...props} className="block px-3 py-2 text-blue-700" />
},
)
const CreatedLinkComponent = createLink(BasicLinkComponent)
export const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
return <CreatedLinkComponent preload="intent" {...props} />
}The Solid guide follows the same architecture with Solid component types and intrinsic anchor props. The anchor component owns presentation, createLink turns it into a Router link, and the final CustomLink adds reusable defaults before forwarding caller props. That symmetry is useful when a design system spans multiple framework packages in this repository. The public shape remains recognizable: users import createLink and LinkComponent from their framework Router package, create an anchor-like component, and then use the resulting component anywhere they would otherwise use Link.
Sources: docs/router/guide/custom-link.md
<CustomLink to="/dashboard/invoices/$invoiceId" params={{ invoiceId: 0 }} />Integrating Third-Party Link Components
Third-party component libraries often provide their own link primitives, menu items, or render-prop APIs. The custom link guide shows createLink wrapping React Aria Components such as Link and MenuItem, and it notes that React Aria Components version 1.11.0 and later works with Router's intent preload prop. That pattern keeps Router responsible for navigation semantics while allowing the third-party component to continue owning library-specific behavior. When the third-party component has render props for className, style, or children, the guide recommends creating a wrapper component first and passing that wrapper to createLink.
Sources: docs/router/guide/custom-link.md
import { createLink } from '@tanstack/react-router'
import { Link as RACLink, MenuItem } from 'react-aria-components'
export const Link = createLink(RACLink)
export const MenuItemLink = createLink(MenuItem)This distinction matters because createLink expects a component that can receive the props Router needs to produce a link. If a design-system component maps href differently, removes anchor props, or exposes styling only through callbacks, put that adaptation inside a small wrapper. The Router-facing export should be the result of createLink, not a component that manually reconstructs navigation. That keeps active matching, preloading, params, and search typing aligned with Router's own Link behavior while still letting the design system control visual state and interaction details.
Sources: docs/router/guide/custom-link.md
Sharing Typed Link Options
linkOptions is designed for navigation data that appears in more than one place. The guide starts with a dashboard options object and explains that a plain object can infer to as string, which is too broad for route-specific checking. By wrapping the object in linkOptions, the definition is checked immediately and the exact input type is preserved. The same object can then be spread into Link, passed to a navigate function, or thrown through redirect from a route lifecycle function such as beforeLoad.
Sources: docs/router/guide/link-options.md, docs/router/api/router/linkOptions.md
const dashboardLinkOptions = linkOptions({
to: '/dashboard',
search: { search: '' },
})
function DashboardComponent() {
return <Link {...dashboardLinkOptions} />
}The API reference frames linkOptions as accepting props intended for Link, navigate, or redirect and returning an object literal with the exact inferred input type. It describes the prop type in terms of LinkProps and React ref attributes for an anchor element. In practice, this means linkOptions is not a runtime transformation step for navigation data. It is a type-checking boundary that lets you name shared navigation definitions close to their source, catch mistakes earlier, and pass the same object through several Router APIs without losing its narrow route information.
Sources: docs/router/api/router/linkOptions.md, docs/router/guide/link-options.md
Arrays are an important use case because navigation bars are usually rendered by mapping data. The guide shows linkOptions accepting an array of objects for a dashboard menu. Each entry can include a route destination and Router options such as activeOptions, while also carrying app-specific fields like label. The example then maps over the array and spreads each option into Link while using the label for display. Because the inferred input is returned, non-Link fields can remain available to the UI without being erased from the local data model.
Sources: docs/router/guide/link-options.md
API Reference
| API | Input | Output or behavior | Use when |
|---|---|---|---|
| createLink | An anchor-like component from your app or a third-party library | A Router-aware Link component with the same type parameters as Link | You need reusable styling, defaults, or design-system integration |
| linkOptions | A Link-style options object or array intended for Link, navigate, or redirect | The exact inferred input, after type checking | You want shared navigation constants checked before use |
| useLinkProps | Active link options plus React anchor attributes | React anchor attributes safe to apply to an anchor element | You need generated anchor props instead of a component factory |
The main implementation choice is where to put abstraction. If the concern is visual or behavioral presentation, prefer createLink so the component remains the reusable unit. If the concern is a route target, search object, params object, active matching configuration, or redirect target, prefer linkOptions so the navigation definition remains the reusable unit. If you are writing a very custom React component that must render its own anchor but still needs Router's generated href and event behavior, useLinkProps gives you the lower-level props object described in the API reference.
Sources: docs/router/guide/custom-link.md, docs/router/guide/link-options.md, docs/router/api/router/useLinkPropsHook.md
Execution Flow and Next Steps
A practical workflow is to start with the default Link until repeated patterns become obvious. Once the same class names, preload defaults, analytics behavior, or design-system wrappers appear in multiple places, extract a custom component with createLink. Once the same destination object appears in menus, redirects, and button handlers, extract it with linkOptions. Keep these abstractions small and route-aware: the custom component should forward Router props, and the options object should be defined through linkOptions before being consumed elsewhere.
Sources: docs/router/guide/custom-link.md, docs/router/guide/link-options.md
For app navigation, pair this page with the broader Link and Navigation API reference, the navigation-and-links guide, and route-specific pages for params and search params. Custom links are most useful after the route tree is already typed, because that is what lets Router validate destinations and required params in reusable components. Link options are most useful when navigation data becomes shared application configuration, such as dashboard tabs, account menus, or redirects from protected routes. Treat both patterns as ways to scale Router's type safety rather than bypass it.