---
title: "Framework Routing Adapters"
description: "Framework Routing Adapters provide a unified abstraction layer that decouples Fumadocs UI components from underlying framework-specific routing and navigation primitives. By defining a common contr..."
last_updated: "2026-10-12T07:53:59.827853+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/core-architecture/framework-routing-adapters"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/core/src/framework/waku.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/waku.tsx)
- [packages/core/src/framework/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx)
- [packages/core/src/framework/react-router.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/react-router.tsx)
- [packages/radix-ui/src/layouts/notebook/slots/sidebar.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/notebook/slots/sidebar.tsx)
- [packages/core/src/framework/tanstack.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/tanstack.tsx)
- [packages/base-ui/src/layouts/notebook/slots/sidebar.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/layouts/notebook/slots/sidebar.tsx)
- [packages/base-ui/src/provider/waku.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/waku.tsx)
- [packages/core/src/framework/next.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/next.tsx)
- [packages/radix-ui/src/provider/react-router.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/react-router.tsx)
- [packages/radix-ui/src/provider/waku.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/waku.tsx)
- [packages/base-ui/src/provider/react-router.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/react-router.tsx)
- [packages/create-app/src/transform/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/src/transform/index.ts)
- [packages/core/src/dynamic-link.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/dynamic-link.tsx)
- [packages/preview/src/waku.server.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/waku.server.tsx)
- [packages/mdx/src/loaders/adapter.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/loaders/adapter.ts)
- [packages/base-ui/src/provider/tanstack.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/tanstack.tsx)
- [packages/create-app/src/transform/react-router.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/src/transform/react-router.ts)
- [packages/radix-ui/src/provider/tanstack.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/tanstack.tsx)
- [packages/base-ui/src/layouts/flux/slots/sidebar.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/layouts/flux/slots/sidebar.tsx)
- [packages/radix-ui/src/layouts/flux/slots/sidebar.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/flux/slots/sidebar.tsx)
- [packages/mdx/src/runtime/browser.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/browser.tsx)
- [packages/preview/src/components/provider.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/components/provider.tsx)
- [packages/core/package.json](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/package.json)
- [packages/base-ui/src/provider/next.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/next.tsx)
- [packages/preview/src/pages/...slugs.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx)
- [packages/radix-ui/src/layouts/shared/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/shared/index.tsx)
- [packages/radix-ui/src/provider/next.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/next.tsx)
- [packages/asyncapi/src/ui/contexts/api.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/contexts/api.tsx)
- [packages/create-app/src/constants.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/create-app/src/constants.ts)
- [packages/base-ui/src/layouts/shared/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/layouts/shared/index.tsx)
</details>

## Overview

Framework Routing Adapters provide a unified abstraction layer that decouples Fumadocs UI components from underlying framework-specific routing and navigation primitives. By defining a common contract for path resolution, parameter extraction, and navigation hooks, Fumadocs supports multiple meta-frameworks without binding its core logic to any single ecosystem.

Sources: [packages/core/src/framework/index.tsx:29-41](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L29-L41)

## Core Framework Abstraction Interface

### Public API Contract and Framework Context

The core abstraction layer defines the structural contract for framework integration in `fumadocs-core`. The `Framework` interface specifies required navigation and routing functions alongside optional component overrides for links and images. A React context initialized via `createContext` stores this interface, defaulting to `notImplemented` stubs that throw an error prompting developers to wrap their application inside `FrameworkProvider`.

```tsx
export interface Router {
  push: (url: string) => void | Promise<void>;
  refresh: () => void | Promise<void>;
}

export interface Framework {
  usePathname: () => string;
  useParams: () => Record<string, string | string[]>;
  useRouter: () => Router;

  Link?: FC<
    ComponentProps<'a'> & {
      prefetch?: boolean;
    }
  >;

  Image?: FC<ImageProps>;
}

const notImplemented = () => {
  throw new Error('You need to wrap your application inside `FrameworkProvider`.');
};

const FrameworkContext = createContext<Framework>({
  useParams: notImplemented,
  useRouter: notImplemented,
  usePathname: notImplemented,
});
```
Sources: [packages/core/src/framework/index.tsx:24-51](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L24-L51)

| Member | Type | Purpose |
| :--- | :--- | :--- |
| `usePathname` | `() => string` | Returns the current URL pathname string |
| `useParams` | `() => Record<string, string \| string[]>` | Extracts dynamic route parameters as a key-value record |
| `useRouter` | `() => Router` | Provides navigation primitives (`push` and `refresh`) |
| `Link` | `FC<ComponentProps<'a'> & { prefetch?: boolean }>` | Optional framework-specific anchor component override |
| `Image` | `FC<ImageProps>` | Optional framework-specific image component override |

Sources: [packages/core/src/framework/index.tsx:29-41](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L29-L41)

> [!WARNING]
> Failing to wrap component trees in `FrameworkProvider` triggers a runtime error when any hook or core component attempts to access framework context, because the default stub functions throw immediately upon invocation.
> 
> Sources: [packages/core/src/framework/index.tsx:43-51](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L43-L51)

### Provider Memoization and Routing Hooks

The `FrameworkProvider` component accepts `Link`, `useRouter`, `useParams`, `usePathname`, `Image`, and `children`. It wraps its children with `FrameworkContext.Provider` (rendered as `<FrameworkContext value={framework}>`) utilizing `useMemo` to memoize the context object based on its provider dependencies. Individual custom hooks (`usePathname`, `useRouter`, `useParams`) consume this context using React's `use` hook to extract the active framework implementation.

```tsx
export function FrameworkProvider({
  Link,
  useRouter,
  useParams,
  usePathname,
  Image,
  children,
}: Framework & { children: ReactNode }) {
  const framework = useMemo(
    () => ({
      usePathname,
      useRouter,
      Link,
      Image,
      useParams,
    }),
    [Link, usePathname, useRouter, useParams, Image],
  );

  return <FrameworkContext value={framework}>{children}</FrameworkContext>;
}

export function usePathname() {
  return use(FrameworkContext).usePathname();
}

export function useRouter() {
  return use(FrameworkContext).useRouter();
}

export function useParams() {
  return use(FrameworkContext).useParams();
}
```
Sources: [packages/core/src/framework/index.tsx:53-85](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L53-L85)

> [!TIP]
> The framework context consumption relies on React's `use` API, allowing hooks to be safely called within consumer components as long as a valid `FrameworkProvider` exists higher in the tree.
> 
> Sources: [packages/core/src/framework/index.tsx:75-85](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L75-L85)

### Component Fallbacks for Images and Links

The core package provides robust fallback implementations for `Image` and `Link` components when no framework-specific override is supplied through the context. The `Image` component checks for a registered context `Image` component; if absent, it falls back to a standard `<img>` element mapping the `priority` boolean prop to a `fetchPriority` attribute (`high` or `auto`). Similarly, the `Link` component falls back to a standard `<a>` element when no framework link implementation is provided, destructuring out the `prefetch` prop to prevent invalid DOM attribute warnings.

```tsx
export function Image(props: ImageProps) {
  const { Image } = use(FrameworkContext);
  if (!Image) {
    const { src, alt, priority, ...rest } = props;

    return (
      <img alt={alt} src={src as string} fetchPriority={priority ? 'high' : 'auto'} {...rest} />
    );
  }

  return <Image {...props} />;
}

export function Link(props: LinkProps) {
  const { Link } = use(FrameworkContext);
  if (!Link) {
    const { href, prefetch: _, ...rest } = props;
    return <a href={href} {...rest} />;
  }

  return <Link {...props} />;
}
```
Sources: [packages/core/src/framework/index.tsx:87-108](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/index.tsx#L87-L108)

## Next.js Routing Adapter Implementation

### NextProvider and App Router Primitives

The Next.js framework adapter bridges Next.js App Router navigation hooks and UI components into the core framework context. Defined in `packages/core/src/framework/next.tsx`, the `NextProvider` component marks itself with `'use client'` and passes Next.js routing hooks—`usePathname`, `useRouter`, and `useParams` from `next/navigation`—directly into the underlying `FrameworkProvider`. It also accepts optional custom overrides for `Link` and `Image`, defaulting to `next/link` and `next/image` when custom components are omitted.

```tsx
'use client';
import { type Framework, FrameworkProvider } from '@/framework/index';
import type { ReactNode } from 'react';
import { useParams, usePathname, useRouter } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';

export function NextProvider({
  children,
  Link: CustomLink,
  Image: CustomImage,
}: {
  children: ReactNode;
  Link?: Framework['Link'];
  Image?: Framework['Image'];
}) {
  return (
    <FrameworkProvider
      usePathname={usePathname}
      useRouter={useRouter}
      useParams={useParams}
      Link={CustomLink ?? (Link as Framework['Link'])}
      Image={CustomImage ?? (Image as Framework['Image'])}
    >
      {children}
    </FrameworkProvider>
  );
```
Sources: [packages/core/src/framework/next.tsx:1-27](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/next.tsx#L1-L27)

| Prop | Type | Default Value | Purpose |
| :--- | :--- | :--- | :--- |
| `children` | `ReactNode` | (Required) | React subtree wrapped by the provider |
| `Link` | `Framework['Link']` | `next/link` | Custom anchor component override |
| `Image` | `Framework['Image']` | `next/image` | Custom image component override |

Sources: [packages/core/src/framework/next.tsx:8-27](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/next.tsx#L8-L27)

> [!NOTE]
> `NextProvider` relies on Next.js client-side navigation modules (`next/navigation`, `next/link`, `next/image`), requiring all consuming tree branches to execute within a Client Component boundary (`'use client'`).
> 
> Sources: [packages/core/src/framework/next.tsx:1-6](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/next.tsx#L1-L6)

### UI Package Integration Wrappers

The Radix UI and Base UI packages provide their own `RootProvider` components that encapsulate `NextProvider` alongside their respective design system providers (`BaseProvider`). Both `packages/base-ui/src/provider/next.tsx` and `packages/radix-ui/src/provider/next.tsx` accept `RootProviderProps` containing a `components` object with optional `Link` and `Image` overrides, forwarding them to `NextProvider` while spreading remaining props into `BaseProvider`.

```tsx
'use client';
import type { ComponentProps } from 'react';
import { RootProvider as BaseProvider } from '@/provider/base';
import { NextProvider } from 'fumadocs-core/framework/next';
import type { Framework } from 'fumadocs-core/framework';

export interface RootProviderProps extends ComponentProps<typeof BaseProvider> {
  /**
   * Custom framework components to override Next.js defaults
   */
  components?: {
    Link?: Framework['Link'];
    Image?: Framework['Image'];
  };
}

export function RootProvider({ components, ...props }: RootProviderProps) {
  return (
    <NextProvider Link={components?.Link} Image={components?.Image}>
      <BaseProvider {...props}>{props.children}</BaseProvider>
    </NextProvider>
  );
}
```
Sources: [packages/radix-ui/src/provider/next.tsx:1-23](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/next.tsx#L1-L23), [packages/base-ui/src/provider/next.tsx:1-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/next.tsx#L1-L22)

> [!TIP]
> By nesting `BaseProvider` inside `NextProvider`, UI packages ensure that design system primitives and documentation layouts access active Next.js routing context and custom component overrides simultaneously.
> 
> Sources: [packages/radix-ui/src/provider/next.tsx:17-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/next.tsx#L17-L22), [packages/base-ui/src/provider/next.tsx:17-21](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/next.tsx#L17-L21)

## React Router Framework Integration

### Core Bridge and Navigation Hooks

The core React Router adapter translates React Router hooks into the Fumadocs framework interface contract. Located in `packages/core/src/framework/react-router.tsx`, the adapter extracts the current path via `useLocation().pathname` and retrieves route parameters through `useParams()`, casting them to `Record<string, string | string[]>`.

```tsx
const framework: Framework = {
  usePathname() {
    return useLocation().pathname;
  },
  useParams() {
    return useParams() as Record<string, string | string[]>;
  },
```
Sources: [packages/core/src/framework/react-router.tsx:5-11](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/react-router.tsx#L5-L11)

Navigation actions are wrapped inside a memoized `useRouter` hook that combines `useNavigate` and `useRevalidator`. The `push` method delegates directly to `navigate(url)`, while `refresh` triggers `void revalidator.revalidate()`, with both actions dependent on `[navigate, revalidator]`.

```tsx
useRouter() {
  const navigate = useNavigate();
  const revalidator = useRevalidator();

  return useMemo(
    () => ({
      push(url) {
        navigate(url);
      },
      refresh() {
        void revalidator.revalidate();
      },
    }),
    [navigate, revalidator],
  );
},
```
Sources: [packages/core/src/framework/react-router.tsx:12-27](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/react-router.tsx#L12-L27)

The framework's `Link` implementation maps the generic `href` property to React Router's `to` prop and converts boolean `prefetch` flags into `'intent'` or `'none'` string values.

```tsx
Link({ href, prefetch, ...props }) {
  return (
    <Link to={href!} prefetch={prefetch ? 'intent' : 'none'} {...props}>
      {props.children}
    </Link>
  );
},
```
Sources: [packages/core/src/framework/react-router.tsx:28-34](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/react-router.tsx#L28-L34)

> [!NOTE]
> React Router's `useRevalidator` returns a revalidation state object where `revalidate()` is an asynchronous function; the adapter explicitly voids the returned promise to satisfy lint rules within synchronous callback contexts.
> 
> Sources: [packages/core/src/framework/react-router.tsx:21-23](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/react-router.tsx#L21-L23)

### Provider Wrappers and UI Package Integration

The `ReactRouterProvider` component accepts children alongside optional custom `Link` and `Image` overrides, supplying them to the underlying `FrameworkProvider` by falling back to the default framework implementation when custom components are omitted.

```tsx
export function ReactRouterProvider({
  children,
  Link: CustomLink,
  Image: CustomImage,
}: {
  children: ReactNode;
  Link?: Framework['Link'];
  Image?: Framework['Image'];
}) {
  return (
    <FrameworkProvider
      {...framework}
      Link={CustomLink ?? framework.Link}
      Image={CustomImage ?? framework.Image}
    >
      {children}
    </FrameworkProvider>
  );
}
```
Sources: [packages/core/src/framework/react-router.tsx:37-55](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/react-router.tsx#L37-L55)

Both Radix UI and Base UI provide specialized `RootProvider` components that bridge React Router into their respective design system providers. Found in `packages/radix-ui/src/provider/react-router.tsx` and `packages/base-ui/src/provider/react-router.tsx`, these providers accept `RootProviderProps` containing an optional `components` object with `Link` and `Image` properties, forwarding them into `ReactRouterProvider` while spreading remaining props into `BaseProvider`.

```tsx
export interface RootProviderProps extends ComponentProps<typeof BaseProvider> {
  /**
   * Custom framework components to override React Router defaults
   */
  components?: {
    Link?: Framework['Link'];
    Image?: Framework['Image'];
  };
}

export function RootProvider({ components, ...props }: RootProviderProps) {
  return (
    <ReactRouterProvider Link={components?.Link} Image={components?.Image}>
      <BaseProvider {...props}>{props.children}</BaseProvider>
    </ReactRouterProvider>
  );
```
Sources: [packages/radix-ui/src/provider/react-router.tsx:7-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/react-router.tsx#L7-L22), [packages/base-ui/src/provider/react-router.tsx:7-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/react-router.tsx#L7-L22)

| Prop | Type | Purpose |
| :--- | :--- | :--- |
| `components` | `{ Link?: Framework['Link']; Image?: Framework['Image']; }` | Custom framework components to override React Router defaults |
| `children` | `ReactNode` | React subtree rendered inside the base design system provider |

Sources: [packages/radix-ui/src/provider/react-router.tsx:7-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/react-router.tsx#L7-L15), [packages/base-ui/src/provider/react-router.tsx:7-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/react-router.tsx#L7-L15)

> [!WARNING]
> UI package root providers for React Router require the `'use client'` directive at the module boundary to ensure React context hooks operate correctly in client-side routing environments.
> 
> Sources: [packages/radix-ui/src/provider/react-router.tsx:1-1](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/react-router.tsx#L1-L1), [packages/base-ui/src/provider/react-router.tsx:1-1](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/react-router.tsx#L1-L1)

## TanStack Start Provider Layer

### TanStack Router Hooks and State Ref Caching

The TanStack Start framework integration bridges TanStack Router primitives into Fumadocs via core definitions in `fumadocs-core`. The framework object configures a custom `Link` component mapping `href` to `to` and setting prefetch behavior to `'intent'` when enabled.

```tsx
const framework: Framework = {
  Link({ href, prefetch = true, ...props }) {
    return (
      <Link to={href} preload={prefetch ? 'intent' : false} {...props}>
        {props.children}
      </Link>
    );
  },
```
Sources: [packages/core/src/framework/tanstack.tsx:5-12](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/tanstack.tsx#L5-L12)

The `usePathname` hook leverages `useRouterState` to select both loading status and location pathname. To prevent layout flickering during route transitions, pathname updates use a persistent `useRef` combined with `useMemo` to return the cached active pathname while loading state is true.

```tsx
usePathname() {
  const { isLoading, pathname } = useRouterState({
    select: (state) => ({
      isLoading: state.isLoading,
      pathname: state.location.pathname,
    }),
  });

  const activePathname = useRef(pathname);
  return useMemo(() => {
    if (isLoading) {
      return activePathname.current;
    }

    activePathname.current = pathname;
    return pathname;
  }, [isLoading, pathname]);
},
```
Sources: [packages/core/src/framework/tanstack.tsx:13-30](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/tanstack.tsx#L13-L30)

> [!NOTE]
> During route transitions when `isLoading` is active, `usePathname` returns the cached reference value rather than the incoming intermediate pathname to maintain stable navigation highlighting.
> 
> Sources: [packages/core/src/framework/tanstack.tsx:21-29](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/tanstack.tsx#L21-L29)

Router actions are wrapped inside a memoized `useRouter` hook that invokes TanStack Router's `useRouter`. The `push` method delegates to `router.navigate({ href: url })` while `refresh` calls `router.invalidate()`.

```tsx
useRouter() {
  const router = useRouter();

  return useMemo(
    () => ({
      push(url) {
        void router.navigate({
          href: url,
        });
      },
      refresh() {
        void router.invalidate();
      },
    }),
    [router],
  );
},
```
Sources: [packages/core/src/framework/tanstack.tsx:31-47](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/tanstack.tsx#L31-L47)

Parameter extraction is handled by `useParams({ strict: false })`, and the entire definition is wrapped by `TanstackProvider` to supply context descendants with framework bindings.

```tsx
  useParams() {
    return useParams({ strict: false });
  },
};

/**
 * Fumadocs adapter for Tanstack Router/Start
 */
export function TanstackProvider({
  children,
  Link: CustomLink,
  Image: CustomImage,
}: {
  children: ReactNode;
  Link?: Framework['Link'];
  Image?: Framework['Image'];
}) {
  return (
    <FrameworkProvider
      {...framework}
      Link={CustomLink ?? framework.Link}
      Image={CustomImage ?? framework.Image}
    >
      {children}
    </FrameworkProvider>
  );
}
```
Sources: [packages/core/src/framework/tanstack.tsx:48-74](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/tanstack.tsx#L48-L74)

### UI Package Root Providers

Both Base UI and Radix UI expose client-side `RootProvider` components that incorporate `TanstackProvider` from `fumadocs-core`. These providers accept optional component overrides for `Link` and `Image` within a `components` prop.

```tsx
export interface RootProviderProps extends ComponentProps<typeof BaseProvider> {
  /**
   * Custom framework components to override Tanstack Router defaults
   */
  components?: {
    Link?: Framework['Link'];
    Image?: Framework['Image'];
  };
}

export function RootProvider({ components, ...props }: RootProviderProps) {
  return (
    <TanstackProvider Link={components?.Link} Image={components?.Image}>
      <BaseProvider {...props}>{props.children}</BaseProvider>
    </TanstackProvider>
  );
```
Sources: [packages/base-ui/src/provider/tanstack.tsx:7-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/tanstack.tsx#L7-L22), [packages/radix-ui/src/provider/tanstack.tsx:7-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/tanstack.tsx#L7-L22)

| Prop | Type | Purpose |
| :--- | :--- | :--- |
| `components` | `{ Link?: Framework['Link']; Image?: Framework['Image']; }` | Custom framework components to override TanStack Router defaults |
| `children` | `ReactNode` | React subtree rendered inside the design system provider |

Sources: [packages/base-ui/src/provider/tanstack.tsx:7-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/tanstack.tsx#L7-L15), [packages/radix-ui/src/provider/tanstack.tsx:7-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/tanstack.tsx#L7-L15)

> [!WARNING]
> Both package root provider files require the `'use client'` directive at the top of the module to operate correctly within React client component trees.
> 
> Sources: [packages/base-ui/src/provider/tanstack.tsx:1-1](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/tanstack.tsx#L1-L1), [packages/radix-ui/src/provider/tanstack.tsx:1-1](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/tanstack.tsx#L1-L1)

## Waku Minimal Router Adapter

### Waku Router Context Bindings and Framework Definitions

The Waku minimal router adapter connects Waku routing primitives into Fumadocs through core definitions in `fumadocs-core`. The framework object implements `usePathname` by calling Waku's `useRouter().path`, while `useParams` returns an empty object memoized with `useMemo` accompanied by a console warning.

```tsx
const framework: Framework = {
  usePathname() {
    return useRouter().path;
  },
  useParams() {
    console.warn('[Fumadocs] useParams() is not supported on Waku');
    return useMemo(() => ({}), []);
  },
```
Sources: [packages/core/src/framework/waku.tsx:7-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/waku.tsx#L7-L14)

> [!WARNING]
> The `useParams` hook on Waku emits a console warning (`[Fumadocs] useParams() is not supported on Waku`) and returns an empty object because route parameters are accessed directly via page props rather than context hooks.
> 
> Sources: [packages/core/src/framework/waku.tsx:11-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/waku.tsx#L11-L14)

Navigation actions are wrapped inside a memoized `useRouter` hook that caches the router instance using a `useRef`. The returned object provides `push(url)` delegating to `routerRef.current.push(url)` and `refresh()` calling `routerRef.current.reload()`.

```tsx
useRouter() {
  const router = useRouter();
  const routerRef = useRef(router);
  routerRef.current = router;

  return useMemo(
    () => ({
      push(url: string) {
        return routerRef.current.push(url);
      },
      refresh() {
        return routerRef.current.reload();
      },
    }),
    [],
  );
},
```
Sources: [packages/core/src/framework/waku.tsx:15-31](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/waku.tsx#L15-L31)

The framework `Link` component maps Fumadocs link properties to Waku's `Link` element, translating `href` to `to` and setting `unstable_prefetchOnEnter` to the `prefetch` boolean value defaulting to `true`.

```tsx
  Link({ href, prefetch = true, ...props }) {
    return (
      <Link to={href!} unstable_prefetchOnEnter={prefetch} {...props}>
        {props.children}
      </Link>
    );
  },
};
```
Sources: [packages/core/src/framework/waku.tsx:32-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/waku.tsx#L32-L39)

The `WakuProvider` component wraps `FrameworkProvider`, accepting optional custom `Link` and `Image` overrides and falling back to the framework defaults.

```tsx
export function WakuProvider({
  children,
  Link: CustomLink,
  Image: CustomImage,
}: {
  children: ReactNode;
  Link?: Framework['Link'];
  Image?: Framework['Image'];
}) {
  return (
    <FrameworkProvider
      {...framework}
      Link={CustomLink ?? framework.Link}
      Image={CustomImage ?? framework.Image}
    >
      {children}
    </FrameworkProvider>
  );
}
```
Sources: [packages/core/src/framework/waku.tsx:41-59](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/framework/waku.tsx#L41-L59)

### UI Provider Wrappers and Preview Integration

Both Base UI and Radix UI expose client-side `RootProvider` components specifically adapted for Waku. These components wrap children with `WakuProvider` and `BaseProvider`, accepting an optional `components` object to override `Link` and `Image`.

```tsx
export interface RootProviderProps extends ComponentProps<typeof BaseProvider> {
  /**
   * Custom framework components to override Waku defaults
   */
  components?: {
    Link?: Framework['Link'];
    Image?: Framework['Image'];
  };
}

export function RootProvider({ components, ...props }: RootProviderProps) {
  return (
    <WakuProvider Link={components?.Link} Image={components?.Image}>
      <BaseProvider {...props}>{props.children}</BaseProvider>
    </WakuProvider>
  );
```
Sources: [packages/base-ui/src/provider/waku.tsx:7-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/waku.tsx#L7-L22), [packages/radix-ui/src/provider/waku.tsx:7-22](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/waku.tsx#L7-L22)

| Prop | Type | Purpose |
| :--- | :--- | :--- |
| `components` | `{ Link?: Framework['Link']; Image?: Framework['Image']; }` | Custom framework components to override Waku defaults |
| `children` | `ReactNode` | React subtree rendered inside the design system provider |

Sources: [packages/base-ui/src/provider/waku.tsx:7-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/provider/waku.tsx#L7-L15), [packages/radix-ui/src/provider/waku.tsx:7-15](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/provider/waku.tsx#L7-L15)

The preview application leverages `fumadocs-ui/provider/waku` within its client provider component and configures dynamic routing inside `[...slugs].tsx`. The markdown compiler processes documents with remark plugins including `remarkGfm`, `remarkMath`, `remarkMdxMermaid`, `remarkHeading`, `remarkNpm`, and `remarkCodeTab`, alongside rehype plugins for `rehypeKatex`, `rehypeCode`, and `rehypeToc`.

```tsx
const compiler = createMarkdownCompiler({
  remarkPlugins: [
    remarkGfm,
    remarkMath,
    remarkMdxMermaid,
    plugin(remarkHeading, { generateToc: false }),
    plugin(remarkNpm, { persist: { id: 'package-manager' } }),
    remarkCodeTab,
  ],
  remarkRehypeOptions: {
    passThrough: ['mdxJsxFlowElement', 'mdxJsxTextElement'],
  },
  rehypePlugins: [
    rehypeKatex,
    plugin(rehypeCode, { lazy: true, fallbackLanguage: 'text' }),
    plugin(rehypeToc, { exportToc: { as: 'data' } }),
  ],
});
```
Sources: [packages/preview/src/pages/...slugs.tsx:29-46](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/%5B...slugs%5D.tsx#L29-L46)

> [!NOTE]
> The preview application uses dynamic configuration (`getConfig` returning `render: 'dynamic'`) and dynamically selects between `docs` and `flux` layout presets based on runtime configuration settings.
> 
> Sources: [packages/preview/src/pages/[...slugs].tsx:76-96](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/[...slugs].tsx#L76-L96), [packages/preview/src/pages/[...slugs].tsx:225-229](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/pages/[...slugs].tsx#L225-L229)

## Dynamic Links and Active Navigation

### Dynamic Link Href Resolution

The `DynamicLink` component extends standard navigation links by substituting route parameters into templated path strings at runtime. By retrieving active route parameters through the `useParams` hook, `DynamicLink` computes updated URLs via `useMemo` before rendering the underlying `Link` element.

```tsx
export const DynamicLink = forwardRef<HTMLAnchorElement, DynamicLinkProps>(
  ({ href, ...props }, ref) => {
    const params = useParams();

    const url = useMemo(() => (href ? updateHref(href, params) : href), [params, href]);

    return <Link ref={ref} href={url} {...props} />;
  },
);
```
Sources: [packages/core/src/dynamic-link.tsx:15-23](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/dynamic-link.tsx#L15-L23)

The underlying `updateHref` function parses parameter placeholders using regular expressions. If a matching key exists within the parameter dictionary, it substitutes the value—joining array segments with slashes if necessary—and preserves trailing slashes from the template pattern.

```tsx
export function updateHref(href: string, params: Record<string, string | string[]>) {
  return href.replace(/\[(.*)]\/?/, (match, key) => {
    const hasEndingSlash = match[match.length - 1] === '/';
    const value = key in params ? params[key] : undefined;
    if (!value) return '';

    const replacement = typeof value === 'string' ? value : value.join('/');
    return hasEndingSlash ? `${replacement}/` : replacement;
  });
}
```
Sources: [packages/core/src/dynamic-link.tsx:27-36](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/dynamic-link.tsx#L27-L36)

> [!WARNING]
> If a route parameter placeholder lacks a matching key in the `params` record, `updateHref` evaluates `value` as undefined and returns an empty string for that segment.
> 
> Sources: [packages/core/src/dynamic-link.tsx:30-31](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/dynamic-link.tsx#L30-L31)

### Active Navigation and Layout Tabs

Layout structures determine active states for navigation links and folder tabs by evaluating the current pathname against registered URLs and page tree nodes. The `isLinkItemActive` function inspects link configuration objects, immediately returning `false` for custom item types or entries configured with `active: 'none'`.

```tsx
export function updateHref(href: string, params: Record<string, string | string[]>) {
  return href.replace(/\[(.*)]\/?/, (match, key) => {
    const hasEndingSlash = match[match.length - 1] === '/';
    const value = key in params ? params[key] : undefined;
    if (!value) return '';

    const replacement = typeof value === 'string' ? value : value.join('/');
    return hasEndingSlash ? `${replacement}/` : replacement;
  });
}
```
Sources: [packages/core/src/dynamic-link.tsx:27-36](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/dynamic-link.tsx#L27-L36)

Layout tabs generated from page tree structures evaluate active states by searching folder hierarchies for matching child page URLs or checking explicit URL sets and normalized path comparisons.

```tsx
export function isLayoutTabActive(tab: LayoutTab, pathname: string) {
  if (tab.$folder) {
    return (
      PageTree.findPath(
        tab.$folder.children,
        (node) => node.type === 'page' && isActive(node.url, pathname),
      ) !== null
    );
  }

  if (tab.urls) {
    return tab.urls.has(normalize(pathname));
  }

  return isActive(tab.url, pathname, true);
}
```
Sources: [packages/radix-ui/src/layouts/shared/index.tsx:146-161](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/shared/index.tsx#L146-L161)

| Property | Type | Default | Purpose |
| :--- | :--- | :--- | :--- |
| `active` | `'url' \| 'nested-url' \| 'none'` | `'url'` | Defines active state matching criteria for navigation items |
| `url` | `string` | — | Target destination URL for navigation and link resolution |
| `external` | `boolean` | `false` | Indicates whether the link points to an external resource |
| `on` | `'menu' \| 'nav' \| 'all'` | `'all'` | Restricts where link items are rendered within the layout |

Sources: [packages/radix-ui/src/layouts/shared/index.tsx:163-181](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/shared/index.tsx#L163-L181)

## Related

- [Shared Layout Architecture](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/ui-layouts/shared-layout-architecture)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
