Next.js Link Integration
This guide explains how to integrate Next.js routing with Gesso components using the linkComponent prop.
Background
In Next.js applications, client-side navigation is handled by the next/link component. When using Gesso components that render links (like Link, Menu, Card, etc.), you need to integrate Next.js routing to enable client-side navigation instead of full page reloads.
The Problem with Nested Anchors
In Next.js 13+, the Link component renders its own <a> tag. Previously, developers used the legacyBehavior and passHref props with a wrapper pattern:
// ❌ Legacy approach - causes nested <a> tags in Next.js 13+
const linkWrapper = (children, url) => (
<Link passHref legacyBehavior href={url ?? '/'}>
{children}
</Link>
);
<Menu linkWrapper={linkWrapper} data={menuData} />
This pattern causes:
- Nested
<a>tags - Invalid HTML that can cause rendering issues - Hydration errors - React may warn about hydration mismatches
- Deprecated patterns -
legacyBehaviorandpassHrefare deprecated in Next.js 16+
The Solution: linkComponent Prop
We now allow developers to pass the Next.js Link component directly via the linkComponent prop. MUI's Link component accepts a component prop that renders the link as the specified component while preserving all MUI styling.
import NextLink from 'next/link';
import { Link } from '@acromedia/gesso';
<Link linkComponent={NextLink} href="/path">
Click me
</Link>
This approach:
- Uses MUI's native
componentprop pattern - Preserves all MUI Link styling (underline, color, hover states, focus states)
- Enables Next.js routing without nested anchors
- Works seamlessly with Next.js 16+
linkComponent with component Prop
When you need custom element styling (e.g., block layout with div) while maintaining Next.js routing:
import NextLink from 'next/link';
import { Link } from '@acromedia/gesso';
// Renders as <NextLink><div>...</div></NextLink>
<Link linkComponent={NextLink} component="div" href="/path">
Click me
</Link>
Behavior Summary
| Props Provided | Rendered Structure |
|---|---|
linkComponent={NextLink} | <a> with Next.js routing |
linkComponent={NextLink} + component="div" | <a><div>...</div></a> (NextLink wraps div) |
component="div" (no linkComponent) | <div> element |
Updated Components
The linkComponent prop has been added to the following components:
Core Components
Link- Base link componentMenu- Navigation menusNavigationBar- Main navigation barHeaderToolbar- Site header toolbarBreadcrumbs- Breadcrumb navigationCard- Card with link actionsProductCard- E-commerce product cardsCartItem- Shopping cart itemsCategoryNavigation- Category navigation lists
Layout Components
AccountSignInForm- Sign-in form with forgot password/create account linksCheckoutInfoPane- Checkout information panelsCheckoutReview- Checkout review pageBlogPageDetails- Blog post detail pagesBlogPageRelatedArticles- Related articles sectionAddToCart- Add to cart component with cart links
Usage Examples
Basic Link
import NextLink from 'next/link';
import { Link } from '@acromedia/gesso';
function MyComponent() {
return (
<Link
linkComponent={NextLink}
href="/products"
variant="body1"
underline="hover"
>
View Products
</Link>
);
}
Navigation Menu
import NextLink from 'next/link';
import { Menu } from '@acromedia/gesso';
const menuData = [
{ id: '1', title: 'Home', url: '/' },
{ id: '2', title: 'Products', url: '/products' },
{ id: '3', title: 'About', url: '/about' },
];
function MainNav() {
return (
<Menu
data={menuData}
linkComponent={NextLink}
orientation="horizontal"
variant="primary"
/>
);
}
Header Toolbar
import NextLink from 'next/link';
import { HeaderToolbar } from '@acromedia/gesso';
function Header() {
return (
<HeaderToolbar
linkComponent={NextLink}
logoLink="/"
primaryMenuData={menuData}
// ... other props
/>
);
}
Link with Custom Element (div)
import NextLink from 'next/link';
import { Link } from '@acromedia/gesso';
function BlockLink() {
return (
<Link
linkComponent={NextLink}
component="div"
href="/example"
underline="none"
>
This is a block-level link with div styling
</Link>
);
}
Migration Guide
From linkWrapper to linkComponent
If you're currently using the linkWrapper prop, here's how to migrate:
Before (Legacy):
import Link from 'next/link';
const nextLinkWrapper = (children, url) => (
<Link passHref legacyBehavior href={url ?? '/'}>
{children}
</Link>
);
<Menu linkWrapper={nextLinkWrapper} data={menuData} />
<HeaderToolbar linkWrapper={nextLinkWrapper} />
<Card linkWrapper={nextLinkWrapper} />
After (Recommended):
import NextLink from 'next/link';
<Menu linkComponent={NextLink} data={menuData} />
<HeaderToolbar linkComponent={NextLink} />
<Card linkComponent={NextLink} />
Key Changes
-
Import: Rename the import to avoid confusion with Gesso's Link
import NextLink from 'next/link'; -
Replace prop: Change
linkWrapper={nextLinkWrapper}tolinkComponent={NextLink} -
Remove wrapper function: Delete the
nextLinkWrapperfunction entirely
Precedence and Compatibility
Using Both linkWrapper and linkComponent
Both linkWrapper and linkComponent props are supported for backwards compatibility. However, if both are provided:
linkComponenttakes precedence- A warning is logged in development to help identify unintentional usage
// ⚠️ Development warning: Both props provided - linkComponent will be used
<Link
linkWrapper={myWrapper} // Ignored
linkComponent={NextLink} // Used
href="/about"
>
About
</Link>
The warning message in development:
Link: Both `linkComponent` and `linkWrapper` props were provided.
`linkComponent` takes precedence. Consider using only one approach.
Migration Notes
- The
linkWrapperpattern is still supported for backwards compatibility - For Next.js 16+, use
linkComponent={NextLink}instead oflinkWrapper - When using
linkComponent, the element must be an anchor-compatible component (like Next.js Link)
TypeScript Support
All components have full TypeScript support for the linkComponent prop:
import type { ElementType } from 'react';
import NextLink from 'next/link';
import { Link, LinkProps } from '@acromedia/gesso';
// linkComponent accepts React.ElementType
const MyLink = (props: LinkProps) => (
<Link
linkComponent={NextLink}
{...props}
/>
);
Troubleshooting
Hydration Errors
If you see hydration errors related to nested <a> tags, ensure you're using linkComponent instead of linkWrapper:
// ❌ May cause hydration errors in Next.js 13+
<Link linkWrapper={legacyWrapper} href="/about">About</Link>
// ✅ No hydration errors
<Link linkComponent={NextLink} href="/about">About</Link>
Links Not Navigating Client-Side
Ensure you're passing the Next.js Link component directly, not calling it:
// ❌ Wrong - calling the component
<Link linkComponent={NextLink()} href="/about">About</Link>
// ✅ Correct - passing the component reference
<Link linkComponent={NextLink} href="/about">About</Link>
Console Warning About Both Props
If you see the warning about both linkWrapper and linkComponent being provided, remove the linkWrapper prop:
// ⚠️ Triggers warning
<Menu linkWrapper={oldWrapper} linkComponent={NextLink} />
// ✅ No warning
<Menu linkComponent={NextLink} />
Need Block-Level Element?
If you need block-level styling with routing, use component="div" with linkComponent:
// ✅ Renders as <a><div>...</div></a> with Next.js routing
<Link linkComponent={NextLink} component="div" href="/about">
Block-level content
</Link>