Skip to main content

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 - legacyBehavior and passHref are 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 component prop 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 ProvidedRendered 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 component
  • Menu - Navigation menus
  • NavigationBar - Main navigation bar
  • HeaderToolbar - Site header toolbar
  • Breadcrumbs - Breadcrumb navigation
  • Card - Card with link actions
  • ProductCard - E-commerce product cards
  • CartItem - Shopping cart items
  • CategoryNavigation - Category navigation lists

Layout Components

  • AccountSignInForm - Sign-in form with forgot password/create account links
  • CheckoutInfoPane - Checkout information panels
  • CheckoutReview - Checkout review page
  • BlogPageDetails - Blog post detail pages
  • BlogPageRelatedArticles - Related articles section
  • AddToCart - Add to cart component with cart links

Usage Examples

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>
);
}
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
/>
);
}
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

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

  1. Import: Rename the import to avoid confusion with Gesso's Link

    import NextLink from 'next/link';
  2. Replace prop: Change linkWrapper={nextLinkWrapper} to linkComponent={NextLink}

  3. Remove wrapper function: Delete the nextLinkWrapper function entirely

Precedence and Compatibility

Both linkWrapper and linkComponent props are supported for backwards compatibility. However, if both are provided:

  • linkComponent takes 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 linkWrapper pattern is still supported for backwards compatibility
  • For Next.js 16+, use linkComponent={NextLink} instead of linkWrapper
  • 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>

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>