Relevant source files
The following files were used as context for generating this wiki page:
- packages/core/src/framework/waku.tsx
- packages/core/src/framework/index.tsx
- packages/core/src/framework/react-router.tsx
- packages/radix-ui/src/layouts/notebook/slots/sidebar.tsx
- packages/core/src/framework/tanstack.tsx
- packages/base-ui/src/layouts/notebook/slots/sidebar.tsx
- packages/base-ui/src/provider/waku.tsx
- packages/core/src/framework/next.tsx
- packages/radix-ui/src/provider/react-router.tsx
- packages/radix-ui/src/provider/waku.tsx
- packages/base-ui/src/provider/react-router.tsx
- packages/create-app/src/transform/index.ts
- packages/core/src/dynamic-link.tsx
- packages/preview/src/waku.server.tsx
- packages/mdx/src/loaders/adapter.ts
- packages/base-ui/src/provider/tanstack.tsx
- packages/create-app/src/transform/react-router.ts
- packages/radix-ui/src/provider/tanstack.tsx
- packages/base-ui/src/layouts/flux/slots/sidebar.tsx
- packages/radix-ui/src/layouts/flux/slots/sidebar.tsx
- packages/mdx/src/runtime/browser.tsx
- packages/preview/src/components/provider.tsx
- packages/core/package.json
- packages/base-ui/src/provider/next.tsx
- packages/preview/src/pages/...slugs.tsx
- packages/radix-ui/src/layouts/shared/index.tsx
- packages/radix-ui/src/provider/next.tsx
- packages/asyncapi/src/ui/contexts/api.tsx
- packages/create-app/src/constants.ts
- packages/base-ui/src/layouts/shared/index.tsx
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
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.
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
| 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
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.
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.
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
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.
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.
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
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.
'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
| 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
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').
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.
'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, packages/base-ui/src/provider/next.tsx:1-22
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, packages/base-ui/src/provider/next.tsx:17-21
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[]>.
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
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].
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
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.
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
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.
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.
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
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.
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, packages/base-ui/src/provider/react-router.tsx:7-22
| 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, packages/base-ui/src/provider/react-router.tsx:7-15
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, packages/base-ui/src/provider/react-router.tsx:1-1
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.
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
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.
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
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.
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().
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
Parameter extraction is handled by useParams({ strict: false }), and the entire definition is wrapped by TanstackProvider to supply context descendants with framework bindings.
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
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.
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, packages/radix-ui/src/provider/tanstack.tsx:7-22
| 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, packages/radix-ui/src/provider/tanstack.tsx:7-15
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, packages/radix-ui/src/provider/tanstack.tsx:1-1
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.
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
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.
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().
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
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.
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
The WakuProvider component wraps FrameworkProvider, accepting optional custom Link and Image overrides and falling back to the framework defaults.
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
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.
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, packages/radix-ui/src/provider/waku.tsx:7-22
| 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, packages/radix-ui/src/provider/waku.tsx:7-15
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.
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
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, packages/preview/src/pages/[...slugs].tsx:225-229
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.
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
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.
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
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.
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'.
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
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.
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
| 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
Related
On This Page
OverviewCore Framework Abstraction InterfacePublic API Contract and Framework ContextProvider Memoization and Routing HooksComponent Fallbacks for Images and LinksNext.js Routing Adapter ImplementationNextProvider and App Router PrimitivesUI Package Integration WrappersReact Router Framework IntegrationCore Bridge and Navigation HooksProvider Wrappers and UI Package IntegrationTanStack Start Provider LayerTanStack Router Hooks and State Ref CachingUI Package Root ProvidersWaku Minimal Router AdapterWaku Router Context Bindings and Framework DefinitionsUI Provider Wrappers and Preview IntegrationDynamic Links and Active NavigationDynamic Link Href ResolutionActive Navigation and Layout TabsRelated