How to Configure Astryx Link Component for Next.js, React Router, and Other React Routers

Astryx's Link component is router-agnostic and renders a native <a> by default, but you can replace it with any React router's link component via the LinkProvider pattern without changing your API.

The Astryx design system (facebook/astryx) provides a flexible, context-based solution for integrating its Link component with different routing libraries. Whether you're using Next.js, React Router, TanStack Router, or another solution, you can swap the underlying rendering behavior while preserving Astryx's styling, accessibility features, and tooltip support.

Astryx uses three core pieces to enable router flexibility, all located in packages/core/src/Link/:

  • LinkProvider.tsx – Supplies a custom link component to any subtree via React context
  • useLinkComponent.ts – Resolves which component to render and handles prop mapping
  • Link.tsx – The consumer that applies Astryx's visual and behavioral layer

The resolution hierarchy in useLinkComponent.ts follows this priority: per-instance as prop > provider component > native <a>. When a custom component replaces the default anchor, Astryx automatically adds a to prop (mirroring href) so routers expecting to work without adapter code.

Next.js uses next/link, which expects an href prop. Since Astryx also uses href natively, the integration is straightforward.

Wrap your root layout or app component with LinkProvider:

// app/layout.tsx (Next.js 13+ App Router)
import {LinkProvider} from '@astryxdesign/core';
import NextLink from 'next/link';

export default function RootLayout({children}: {children: React.ReactNode}) {
  return (
    <html>
      <body>
        <LinkProvider component={NextLink}>
          {children}
        </LinkProvider>
      </body>
    </html>
  );
}

Now all Astryx Link components render as next/link instances:

import {Link} from '@astryxdesign/core';

export function Navigation() {
  return (
    <nav>
      <Link href="/products">Products</Link>
      <Link href="/docs" isExternalLink>Documentation</Link>
    </nav>
  );
}

The isExternalLink prop still works correctly—useLinkComponent.ts detects external URLs and appropriately falls back to standard anchor behavior even when a custom component is provided.

React Router's <Link> component expects a to prop instead of href. Astryx handles this automatically through createLinkWithTo in useLinkComponent.ts, which wraps custom components to inject to={href}.

Set up your app with BrowserRouter and LinkProvider:

// src/App.tsx
import {BrowserRouter} from 'react-router-dom';
import {LinkProvider} from '@astryxdesign/core';
import {Link as RouterLink} from 'react-router-dom';

export function App() {
  return (
    <BrowserRouter>
      <LinkProvider component={RouterLink}>
        {/* Your application routes */}
      </LinkProvider>
    </BrowserRouter>
  );
}

Write navigation using standard Astryx href props:

import {Link} from '@astryxdesign/core';

export function AdminSidebar() {
  return (
    <aside>
      <ul>
        <li><Link href="/dashboard">Dashboard</Link></li>
        <li><Link href="/users">User Management</Link></li>
        <li><Link href="/reports">Reports</Link></li>
      </ul>
    </aside>
  );
}

Behind the scenes in useLinkComponent.ts, the wrapper translates your href into both href and to props, satisfying React Router's requirements without code changes.

Per-Instance Component Override with the as Prop

For mixed routing scenarios or one-off exceptions, use the as prop to override the component for a single Link instance. This bypasses both the provider value and the default anchor.

import {Link} from '@astryxdesign/core';
import {Link as RouterLink} from 'react-router-dom';
import NextLink from 'next/link';

export function HybridNavigation() {
  return (
    <>
      {/* Uses provider default (Next.js in this example) */}
      <Link href="/home">Home</Link>
      
      {/* Overrides to React Router Link for this instance only */}
      <Link href="/admin" as={RouterLink}>Admin Panel</Link>
      
      {/* Forces native anchor despite provider */}
      <Link href="/legacy" as="a">Legacy Page</Link>
    </>
  );
}

The as prop accepts any valid React component type or the string 'a', giving you granular control when needed.

Key Source Files in Astryx

Understanding the implementation details helps debug integration issues:

File Purpose
LinkProvider.tsx Context provider that stores the custom link component
useLinkComponent.ts Hook resolving component priority and injecting to prop
Link.tsx Core component applying theme, transitions, and accessibility
LinkContext.ts React context definition for provider/consumer pair
Link.doc.mjs API documentation and prop specifications

The source code reveals that useLinkComponent uses the experimental use(LinkContext) API (React 18+) for context reading, and that createLinkWithTo is only applied when the resolved component isn't the native 'a' string.

Complete TypeScript Example: Multi-Router Setup

Here's a practical pattern for applications transitioning between routers or using micro-frontends with different routing solutions:

// components/RouterAwareLink.tsx
import {Link as AstryxLink, LinkProps} from '@astryxdesign/core';
import {Link as RouterLink} from 'react-router-dom';
import NextLink from 'next/link';

type RouterType = 'next' | 'react-router' | 'default';

interface RouterAwareLinkProps extends Omit<LinkProps, 'as'> {
  router?: RouterType;
}

const routerComponents: Record<RouterType, React.ComponentType<any>> = {
  next: NextLink,
  'react-router': RouterLink,
  default: 'a',
};

export function RouterAwareLink({router = 'default', ...props}: RouterAwareLinkProps) {
  return <AstryxLink {...props} as={routerComponents[router]} />;
}

Usage across your application remains consistent:

// In a Next.js page
<RouterAwareLink router="next" href="/about">About</RouterAwareLink>

// In a React Router subtree
<RouterAwareLink router="react-router" href="/profile">Profile</RouterAwareLink>

// External or static links
<RouterAwareLink href="https://example.com" isExternalLink>External</RouterAwareLink>

Summary

  • Astryx Link is router-agnostic by design — start with a native <a> and upgrade to framework-specific components via LinkProvider
  • LinkProvider sets the default for all descendant links through React context defined in LinkContext.ts
  • useLinkComponent handles the mapping — resolves priority chain (as > provider > 'a') and auto-injects to for React Router compatibility
  • No adapter code needed — the internal createLinkWithTo wrapper in useLinkComponent.ts bridges href and to prop differences automatically
  • Granular control available — the as prop on individual Link instances overrides context for mixed-router scenarios

Frequently Asked Questions

Yes. TanStack Router's Link component uses a to prop like React Router, so the same configuration pattern applies. Wrap your app with LinkProvider and pass TanStack's Link component—the automatic to injection in useLinkComponent.ts handles the rest.

Can I use different routers in the same application?

Absolutely. The as prop on individual Link instances overrides the LinkProvider value. You can nest multiple providers at different tree levels, or use per-instance overrides to mix Next.js, React Router, and native links as needed.

What happens if I don't configure a provider?

Without LinkProvider, all Astryx Link components render as native <a> elements. You retain full styling, accessibility, and tooltip functionality, but lose client-side navigation benefits from your framework's router.

Yes. The external link detection logic runs before component resolution in Link.tsx. External URLs automatically render as native anchors with appropriate rel and target attributes, regardless of your LinkProvider configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →