How to Use React MUI Link to Build Styled Inline Navigation Links
Navigation is a critical aspect of any web application, and having well-styled, accessible links can significantly enhance user experience. Material UI's Link component offers a powerful way to create these navigation elements while maintaining your application's design language. In this article, I'll walk you through everything you need to know about using the MUI Link component to build effective inline navigation.
As a developer who has implemented countless navigation systems across various React applications, I've found that mastering the MUI Link component opens up numerous possibilities for creating intuitive user interfaces. Let's dive deep into this component and explore its capabilities, from basic implementation to advanced customization techniques.
What You'll Learn
By the end of this article, you'll be able to:
- Implement basic MUI Link components with proper styling and behavior
- Understand and utilize all critical props and variants
- Integrate MUI Links with React Router and Next.js for seamless navigation
- Create custom styled links that match your application's design system
- Implement accessible navigation that works for all users
- Avoid common pitfalls and performance issues
- Build complex navigation patterns with the Link component
Understanding the MUI Link Component
The Link component in Material UI is designed to be the primary navigation element in your applications. It extends the Typography component, inheriting many of its styling capabilities while adding navigation-specific features.
At its core, the MUI Link component is a wrapper around the native HTML <a>
element, enhancing it with Material Design styling and additional functionality. This means you get all the standard anchor tag behaviors plus Material Design's visual language and React's component benefits.
What sets the MUI Link apart from a standard HTML anchor is its integration with MUI's theming system, transition effects, and the ability to work seamlessly with routing libraries like React Router or Next.js.
Basic Link Implementation
Let's start with the most basic implementation of the MUI Link component:
import * as React from 'react';
import Link from '@mui/material/Link';
function BasicLink() {
return (
<Link href="https://mui.com/">
Visit Material UI
</Link>
);
}
This renders a simple text link with MUI's default styling. The component handles the underlying HTML anchor tag creation and applies default styles that match Material Design guidelines.
Link Props and Configuration Options
The MUI Link component comes with a rich set of props that allow you to control its appearance and behavior. Here's a comprehensive overview of the most important props:
Prop | Type | Default | Description |
---|---|---|---|
children | node | The content of the link, usually text or an icon | |
color | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | 'inherit' | string | 'primary' | The color of the link |
component | elementType | 'a' | The component used for the root node |
href | string | The URL to link to when the link is clicked | |
underline | 'none' | 'hover' | 'always' | 'always' | Controls when the link should have an underline |
variant | 'body1' | 'body2' | 'button' | 'caption' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'inherit' | 'overline' | 'subtitle1' | 'subtitle2' | string | 'inherit' | Applies the theme typography styles |
sx | object | function | array | The system prop that allows defining system overrides as well as additional CSS styles | |
target | string | Target attribute for the underlying anchor element ('_blank', '_self', etc.) | |
rel | string | Relationship attribute for the underlying anchor element | |
onClick | function | Callback fired when the link is clicked |
Let's see these props in action with a more comprehensive example:
import * as React from 'react';
import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
function LinkVariations() {
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{/* Different colors */}
<Typography variant="h6">Link Colors</Typography>
<Box sx={{ display: 'flex', gap: 2 }}>
<Link href="#" color="primary">Primary</Link>
<Link href="#" color="secondary">Secondary</Link>
<Link href="#" color="error">Error</Link>
<Link href="#" color="info">Info</Link>
<Link href="#" color="success">Success</Link>
<Link href="#" color="warning">Warning</Link>
<Link href="#" color="inherit">Inherit</Link>
</Box>
{/* Different underline styles */}
<Typography variant="h6">Underline Styles</Typography>
<Box sx={{ display: 'flex', gap: 2 }}>
<Link href="#" underline="none">No Underline</Link>
<Link href="#" underline="hover">Underline on Hover</Link>
<Link href="#" underline="always">Always Underlined</Link>
</Box>
{/* Different typography variants */}
<Typography variant="h6">Typography Variants</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Link href="#" variant="h5">Heading Link</Link>
<Link href="#" variant="body1">Body Text Link</Link>
<Link href="#" variant="button">Button Style Link</Link>
<Link href="#" variant="caption">Caption Link</Link>
</Box>
{/* External links with security attributes */}
<Typography variant="h6">External Links</Typography>
<Link
href="https://mui.com"
target="_blank"
rel="noopener noreferrer"
>
External Link (Secure)
</Link>
</Box>
);
}
Link Variants and Visual Configurations
The MUI Link component inherits typography variants from the Typography component, allowing you to create links that match different text styles in your application. Additionally, it offers specific styling options through the underline
and color
props.
Underline Variations
The underline
prop controls when the underline decoration appears:
import * as React from 'react';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
function UnderlineVariations() {
return (
<Stack spacing={2} direction="column">
<Link href="#" underline="none">
This link has no underline
</Link>
<Link href="#" underline="hover">
This link has an underline only on hover
</Link>
<Link href="#" underline="always">
This link is always underlined
</Link>
</Stack>
);
}
The "hover" option is particularly useful for navigation menus where you want to indicate interactivity without visual clutter.
Color Variations
The color
prop allows you to match links to your theme's color palette:
import * as React from 'react';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
function ColorVariations() {
return (
<Stack spacing={2} direction="column">
<Link href="#" color="primary">Primary color link</Link>
<Link href="#" color="secondary">Secondary color link</Link>
<Link href="#" color="error">Error color link</Link>
<Link href="#" color="warning">Warning color link</Link>
<Link href="#" color="info">Info color link</Link>
<Link href="#" color="success">Success color link</Link>
<Link href="#" color="inherit">Inherit color link</Link>
<Link href="#" color="textPrimary">Text primary color link</Link>
<Link href="#" color="textSecondary">Text secondary color link</Link>
</Stack>
);
}
Using semantic colors like "error" or "success" can provide visual cues about the link's purpose or destination.
Integrating MUI Link with React Router
One of the most common use cases for the Link component is integration with React Router for client-side navigation. This approach prevents full page reloads and enables smoother transitions between routes.
Setting Up the Integration
To integrate MUI Link with React Router, you need to use the component
prop to replace the default anchor element with React Router's Link component:
import * as React from 'react';
import { BrowserRouter, Routes, Route, Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
// Sample pages
function Home() {
return <Typography variant="h4">Home Page</Typography>;
}
function About() {
return <Typography variant="h4">About Page</Typography>;
}
function Contact() {
return <Typography variant="h4">Contact Page</Typography>;
}
function RouterIntegration() {
return (
<BrowserRouter>
<Box sx={{ mb: 4 }}>
<Box sx={{ display: 'flex', gap: 3, mb: 2 }}>
<Link component={RouterLink} to="/" underline="hover">
Home
</Link>
<Link component={RouterLink} to="/about" underline="hover">
About
</Link>
<Link component={RouterLink} to="/contact" underline="hover">
Contact
</Link>
</Box>
</Box>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
This setup combines the styling capabilities of MUI Link with the routing functionality of React Router. Note how we use the to
prop instead of href
when using React Router's Link component.
Creating a Custom Link Component
For larger applications, it's often better to create a reusable custom link component that handles both internal and external links:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
// Custom link component that handles both internal and external links
function CustomLink({ children, to, ...props }) {
const isExternal = /^(https?:)?///.test(to) || to.startsWith('mailto:');
if (isExternal) {
return (
<Link
href={to}
target="_blank"
rel="noopener noreferrer"
{...props}
>
{children}
</Link>
);
}
return (
<Link component={RouterLink} to={to} {...props}>
{children}
</Link>
);
}
// Usage example
function App() {
return (
<div>
<CustomLink to="/dashboard" underline="hover">Dashboard</CustomLink>
<CustomLink to="https://example.com" underline="hover">External Site</CustomLink>
</div>
);
}
This approach gives you a unified API for all links in your application while handling the different requirements for internal and external links automatically.
Integrating with Next.js
If you're using Next.js instead of React Router, the integration is similar but uses Next.js's Link component:
import * as React from 'react';
import NextLink from 'next/link';
import MuiLink from '@mui/material/Link';
function NextJsLink() {
return (
<NextLink href="/about" passHref legacyBehavior>
<MuiLink underline="hover">About Page</MuiLink>
</NextLink>
);
}
// For Next.js 13+ with the App Router, you might use:
function NextJsLinkModern() {
return (
<NextLink href="/about">
<MuiLink component="span" underline="hover">About Page</MuiLink>
</NextLink>
);
}
For Next.js 13 and later with the App Router, you might need to adjust this pattern slightly, as shown in the second example.
Custom Styling and Theming MUI Links
Material UI offers several ways to customize the Link component to match your application's design system.
Using the sx Prop for Inline Styling
The sx
prop provides a powerful way to add custom styles directly to your Link components:
import * as React from 'react';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
function StyledLinks() {
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{/* Basic sx styling */}
<Link
href="#"
sx={{
fontWeight: 'bold',
fontSize: '1.2rem',
letterSpacing: 0.5,
textTransform: 'uppercase'
}}
>
Bold Uppercase Link
</Link>
{/* Custom hover effects */}
<Link
href="#"
sx={{
color: 'text.primary',
textDecoration: 'none',
position: 'relative',
'&::after': {
content: '""',
position: 'absolute',
width: '100%',
height: '2px',
bottom: 0,
left: 0,
backgroundColor: 'primary.main',
transform: 'scaleX(0)',
transformOrigin: 'bottom right',
transition: 'transform 0.3s ease-out'
},
'&:hover::after': {
transform: 'scaleX(1)',
transformOrigin: 'bottom left'
}
}}
>
Link with Custom Underline Animation
</Link>
{/* Gradient text */}
<Link
href="#"
underline="none"
sx={{
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
fontWeight: 'bold'
}}
>
Gradient Text Link
</Link>
</Box>
);
}
The sx
prop accepts any valid CSS properties and supports responsive values, pseudo-classes, and nested selectors.
Theme Customization for Links
For application-wide customization, you can modify the MUI theme to change the default styling of all Link components:
import * as React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography';
// Create a custom theme
const theme = createTheme({
components: {
MuiLink: {
defaultProps: {
underline: 'hover', // Change the default underline behavior
},
styleOverrides: {
root: {
fontWeight: 500, // Make all links slightly bolder
'&:hover': {
color: '#ff5722', // Custom hover color
transition: 'color 0.3s ease',
},
},
},
variants: [
{
props: { variant: 'nav' }, // Custom variant
style: {
textTransform: 'uppercase',
fontSize: '0.875rem',
letterSpacing: '0.1em',
padding: '8px 16px',
borderRadius: '4px',
'&:hover': {
backgroundColor: 'rgba(0, 0, 0, 0.04)',
},
},
},
],
},
},
});
function ThemedLinks() {
return (
<ThemeProvider theme={theme}>
<Typography variant="h6">Default Themed Links</Typography>
<Link href="#">Standard themed link</Link>
<Typography variant="h6" sx={{ mt: 2 }}>Custom Variant</Typography>
<Link href="#" variant="nav">Navigation link</Link>
</ThemeProvider>
);
}
This approach ensures consistent styling across your application and enables you to create custom variants for different use cases.
Creating a Styled Link Component
For more complex styling, you can use the styled
API to create a fully customized Link component:
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
// Button-like link
const ButtonLink = styled(Link)(({ theme }) => ({
display: 'inline-block',
padding: '8px 16px',
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
borderRadius: theme.shape.borderRadius,
textDecoration: 'none',
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: '0.5px',
transition: 'background-color 0.3s ease',
'&:hover': {
backgroundColor: theme.palette.primary.dark,
textDecoration: 'none',
},
}));
// Underline animation link
const AnimatedLink = styled(Link)(({ theme }) => ({
position: 'relative',
color: theme.palette.text.primary,
textDecoration: 'none',
'&::before': {
content: '""',
position: 'absolute',
display: 'block',
width: '100%',
height: '2px',
bottom: 0,
left: 0,
backgroundColor: theme.palette.primary.main,
transform: 'scaleX(0)',
transition: 'transform 0.3s ease',
},
'&:hover::before': {
transform: 'scaleX(1)',
},
}));
function CustomStyledLinks() {
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<ButtonLink href="#">Button-style Link</ButtonLink>
<AnimatedLink href="#">Animated Underline Link</AnimatedLink>
</Box>
);
}
This method is particularly useful when you need to reuse the same custom styling in multiple places or when the styling is too complex for the sx
prop.
Building a Navigation Menu with MUI Links
Now let's apply what we've learned to build a practical navigation menu using MUI Links:
import * as React from 'react';
import { BrowserRouter, Routes, Route, Link as RouterLink } from 'react-router-dom';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import { styled } from '@mui/material/styles';
// Sample pages
function Home() {
return <Typography variant="h4">Home Page</Typography>;
}
function Products() {
return <Typography variant="h4">Products Page</Typography>;
}
function Services() {
return <Typography variant="h4">Services Page</Typography>;
}
function About() {
return <Typography variant="h4">About Page</Typography>;
}
function Contact() {
return <Typography variant="h4">Contact Page</Typography>;
}
// Styled nav link
const NavLink = styled(Link)(({ theme }) => ({
color: 'white',
padding: '8px 16px',
textDecoration: 'none',
borderRadius: theme.shape.borderRadius,
transition: 'background-color 0.3s',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)',
textDecoration: 'none',
},
'&.active': {
backgroundColor: 'rgba(255, 255, 255, 0.2)',
fontWeight: 500,
},
}));
function NavigationMenu() {
// Simple active route tracking
const [activeRoute, setActiveRoute] = React.useState(window.location.pathname);
const handleNavClick = (route) => {
setActiveRoute(route);
};
return (
<BrowserRouter>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 0, mr: 4 }}>
MyApp
</Typography>
<Box sx={{ display: 'flex', gap: 1 }}>
<NavLink
component={RouterLink}
to="/"
className={activeRoute === '/' ? 'active' : ''}
onClick={() => handleNavClick('/')}
>
Home
</NavLink>
<NavLink
component={RouterLink}
to="/products"
className={activeRoute === '/products' ? 'active' : ''}
onClick={() => handleNavClick('/products')}
>
Products
</NavLink>
<NavLink
component={RouterLink}
to="/services"
className={activeRoute === '/services' ? 'active' : ''}
onClick={() => handleNavClick('/services')}
>
Services
</NavLink>
<NavLink
component={RouterLink}
to="/about"
className={activeRoute === '/about' ? 'active' : ''}
onClick={() => handleNavClick('/about')}
>
About
</NavLink>
<NavLink
component={RouterLink}
to="/contact"
className={activeRoute === '/contact' ? 'active' : ''}
onClick={() => handleNavClick('/contact')}
>
Contact
</NavLink>
</Box>
</Toolbar>
</AppBar>
<Container sx={{ mt: 4 }}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/services" element={<Services />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Container>
</BrowserRouter>
);
}
This example creates a responsive navigation bar with styled links that show an active state when the user is on the corresponding route.
Accessibility Considerations for MUI Links
Creating accessible navigation is crucial for ensuring your application is usable by everyone. MUI's Link component provides a good foundation, but there are several additional considerations to keep in mind.
ARIA Attributes and Keyboard Navigation
Enhance the accessibility of your links with appropriate ARIA attributes:
import * as React from 'react';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
function AccessibleLinks() {
return (
<Box>
{/* Link with additional description for screen readers */}
<Link
href="/pricing"
aria-label="View our pricing plans and subscription options"
>
Pricing
</Link>
{/* External link with indication */}
<Link
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
aria-label="Visit example site (opens in a new window)"
sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}
>
External Resource
<span aria-hidden="true" style={{ fontSize: '0.8rem' }}>
(opens in new window)
</span>
</Link>
{/* Skip link for keyboard users */}
<Link
href="#main-content"
sx={{
position: 'absolute',
left: '-9999px',
top: 'auto',
width: '1px',
height: '1px',
overflow: 'hidden',
'&:focus': {
position: 'fixed',
top: '0',
left: '0',
width: 'auto',
height: 'auto',
padding: '16px',
backgroundColor: 'background.paper',
zIndex: 'tooltip',
boxShadow: 3,
}
}}
>
Skip to main content
</Link>
</Box>
);
}
Focus Indicators
Ensure your links have visible focus indicators for keyboard users:
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Link from '@mui/material/Link';
// Enhanced focus styles
const AccessibleLink = styled(Link)(({ theme }) => ({
'&:focus-visible': {
outline: `2px solid ${theme.palette.primary.main}`,
outlineOffset: '2px',
borderRadius: '2px',
},
}));
function FocusableLinks() {
return (
<div>
<AccessibleLink href="/page1">Enhanced Focus Link 1</AccessibleLink>
<AccessibleLink href="/page2">Enhanced Focus Link 2</AccessibleLink>
</div>
);
}
Creating Accessible Icon Links
When using icons as links, be sure to provide accessible labels:
import * as React from 'react';
import Link from '@mui/material/Link';
import HomeIcon from '@mui/icons-material/Home';
import InfoIcon from '@mui/icons-material/Info';
import MailIcon from '@mui/icons-material/Mail';
import Box from '@mui/material/Box';
function IconLinks() {
return (
<Box sx={{ display: 'flex', gap: 2 }}>
{/* Icon-only link with aria-label */}
<Link
href="/home"
aria-label="Home page"
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
p: 1,
borderRadius: '50%',
'&:hover': { bgcolor: 'action.hover' }
}}
>
<HomeIcon />
</Link>
{/* Icon with text */}
<Link
href="/about"
sx={{
display: 'flex',
alignItems: 'center',
gap: 0.5
}}
>
<InfoIcon fontSize="small" />
About Us
</Link>
{/* Icon with visually hidden text */}
<Link
href="/contact"
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
p: 1,
borderRadius: '50%',
'&:hover': { bgcolor: 'action.hover' }
}}
>
<MailIcon />
<span style={{
position: 'absolute',
width: '1px',
height: '1px',
padding: '0',
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)',
whiteSpace: 'nowrap',
borderWidth: '0'
}}>
Contact Us
</span>
</Link>
</Box>
);
}
Advanced Link Patterns
Let's explore some advanced patterns for using MUI Links in complex navigation scenarios.
Breadcrumb Navigation
Breadcrumbs help users understand their location in a website hierarchy:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography';
import Breadcrumbs from '@mui/material/Breadcrumbs';
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
function BreadcrumbNavigation({ paths = [] }) {
return (
<Breadcrumbs
separator={<NavigateNextIcon fontSize="small" />}
aria-label="breadcrumb"
>
<Link component={RouterLink} to="/" underline="hover" color="inherit">
Home
</Link>
{paths.map((path, index) => {
const isLast = index === paths.length - 1;
const to = `/${paths.slice(0, index + 1).join('/')}`;
return isLast ? (
<Typography color="text.primary" key={to}>
{path.charAt(0).toUpperCase() + path.slice(1)}
</Typography>
) : (
<Link
component={RouterLink}
to={to}
underline="hover"
color="inherit"
key={to}
>
{path.charAt(0).toUpperCase() + path.slice(1)}
</Link>
);
})}
</Breadcrumbs>
);
}
// Usage example
function ProductPage() {
return (
<div>
<BreadcrumbNavigation paths={['products', 'electronics', 'smartphones']} />
<Typography variant="h4" sx={{ mt: 2 }}>
Smartphones
</Typography>
{/* Rest of the page content */}
</div>
);
}
Dropdown Navigation
Create dropdown navigation menus with MUI Links:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import Box from '@mui/material/Box';
function DropdownNavLink({ title, items }) {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<Button
aria-controls={open ? 'dropdown-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
endIcon={<KeyboardArrowDownIcon />}
sx={{ color: 'white', textTransform: 'none' }}
>
{title}
</Button>
<Menu
id="dropdown-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'dropdown-button',
}}
>
{items.map((item) => (
<MenuItem key={item.path} onClick={handleClose} dense>
<Link
component={RouterLink}
to={item.path}
color="inherit"
underline="none"
sx={{ width: '100%' }}
>
{item.label}
</Link>
</MenuItem>
))}
</Menu>
</div>
);
}
function DropdownNavigation() {
const productItems = [
{ label: 'Electronics', path: '/products/electronics' },
{ label: 'Clothing', path: '/products/clothing' },
{ label: 'Home & Garden', path: '/products/home-garden' },
{ label: 'Books', path: '/products/books' },
];
const servicesItems = [
{ label: 'Consulting', path: '/services/consulting' },
{ label: 'Development', path: '/services/development' },
{ label: 'Design', path: '/services/design' },
{ label: 'Support', path: '/services/support' },
];
return (
<Box sx={{ display: 'flex', bgcolor: 'primary.main', p: 1 }}>
<Link
component={RouterLink}
to="/"
sx={{ color: 'white', p: 1, textDecoration: 'none' }}
>
Home
</Link>
<DropdownNavLink title="Products" items={productItems} />
<DropdownNavLink title="Services" items={servicesItems} />
<Link
component={RouterLink}
to="/about"
sx={{ color: 'white', p: 1, textDecoration: 'none' }}
>
About
</Link>
<Link
component={RouterLink}
to="/contact"
sx={{ color: 'white', p: 1, textDecoration: 'none' }}
>
Contact
</Link>
</Box>
);
}
Pagination with Links
Create accessible pagination using MUI Links:
import * as React from 'react';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { styled } from '@mui/material/styles';
const PaginationLink = styled(Link)(({ theme, active }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '36px',
height: '36px',
borderRadius: '50%',
textDecoration: 'none',
fontWeight: active ? 600 : 400,
color: active ? theme.palette.primary.contrastText : theme.palette.text.primary,
backgroundColor: active ? theme.palette.primary.main : 'transparent',
'&:hover': {
backgroundColor: active ? theme.palette.primary.dark : theme.palette.action.hover,
textDecoration: 'none',
},
}));
function Pagination({ currentPage, totalPages, onPageChange }) {
// Generate page numbers to display
const getPageNumbers = () => {
const pageNumbers = [];
const maxPagesToShow = 5;
if (totalPages <= maxPagesToShow) {
// Show all pages if there are few
for (let i = 1; i <= totalPages; i++) {
pageNumbers.push(i);
}
} else {
// Show a subset of pages with ellipsis
if (currentPage <= 3) {
// Near the start
for (let i = 1; i <= 4; i++) {
pageNumbers.push(i);
}
pageNumbers.push('ellipsis');
pageNumbers.push(totalPages);
} else if (currentPage >= totalPages - 2) {
// Near the end
pageNumbers.push(1);
pageNumbers.push('ellipsis');
for (let i = totalPages - 3; i <= totalPages; i++) {
pageNumbers.push(i);
}
} else {
// In the middle
pageNumbers.push(1);
pageNumbers.push('ellipsis');
for (let i = currentPage - 1; i <= currentPage + 1; i++) {
pageNumbers.push(i);
}
pageNumbers.push('ellipsis');
pageNumbers.push(totalPages);
}
}
return pageNumbers;
};
const pageNumbers = getPageNumbers();
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 1,
my: 3,
}}
role="navigation"
aria-label="Pagination"
>
{/* Previous page link */}
<PaginationLink
href="#"
onClick={(e) => {
e.preventDefault();
if (currentPage > 1) onPageChange(currentPage - 1);
}}
aria-label="Go to previous page"
aria-disabled={currentPage === 1}
tabIndex={currentPage === 1 ? -1 : 0}
sx={{
opacity: currentPage === 1 ? 0.5 : 1,
pointerEvents: currentPage === 1 ? 'none' : 'auto',
}}
>
<
</PaginationLink>
{/* Page number links */}
{pageNumbers.map((page, index) => {
if (page === 'ellipsis') {
return (
<Typography key={`ellipsis-${index}`} sx={{ mx: 1 }}>
…
</Typography>
);
}
return (
<PaginationLink
key={page}
href="#"
active={page === currentPage}
onClick={(e) => {
e.preventDefault();
onPageChange(page);
}}
aria-label={`Go to page ${page}`}
aria-current={page === currentPage ? 'page' : undefined}
>
{page}
</PaginationLink>
);
})}
{/* Next page link */}
<PaginationLink
href="#"
onClick={(e) => {
e.preventDefault();
if (currentPage < totalPages) onPageChange(currentPage + 1);
}}
aria-label="Go to next page"
aria-disabled={currentPage === totalPages}
tabIndex={currentPage === totalPages ? -1 : 0}
sx={{
opacity: currentPage === totalPages ? 0.5 : 1,
pointerEvents: currentPage === totalPages ? 'none' : 'auto',
}}
>
>
</PaginationLink>
</Box>
);
}
// Usage example
function PaginationExample() {
const [currentPage, setCurrentPage] = React.useState(1);
const totalPages = 10;
return (
<div>
<Typography variant="h6">Page {currentPage} of {totalPages}</Typography>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
/>
</div>
);
}
Performance Considerations
When working with links in large applications, consider these performance optimizations:
Memoizing Link Components
For links that are rendered in lists or frequently re-rendered components, use memoization to prevent unnecessary re-renders:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
// Memoized link component
const MemoizedLink = React.memo(function MemoizedLink({ to, children, ...props }) {
return (
<Link component={RouterLink} to={to} {...props}>
{children}
</Link>
);
});
// Usage in a list
function LinkList({ items }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>
<MemoizedLink to={item.path} underline="hover">
{item.label}
</MemoizedLink>
</li>
))}
</ul>
);
}
Lazy Loading Routes
For larger applications, combine MUI Links with lazy-loaded routes:
import * as React from 'react';
import { BrowserRouter, Routes, Route, Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
// Lazy load components
const Home = React.lazy(() => import('./pages/Home'));
const Products = React.lazy(() => import('./pages/Products'));
const About = React.lazy(() => import('./pages/About'));
function LoadingFallback() {
return (
<Box
display="flex"
justifyContent="center"
alignItems="center"
minHeight="200px"
>
<CircularProgress />
</Box>
);
}
function LazyLoadedNavigation() {
return (
<BrowserRouter>
<Box sx={{ mb: 2 }}>
<Link component={RouterLink} to="/" sx={{ mr: 2 }}>Home</Link>
<Link component={RouterLink} to="/products" sx={{ mr: 2 }}>Products</Link>
<Link component={RouterLink} to="/about">About</Link>
</Box>
<React.Suspense fallback={<LoadingFallback />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/about" element={<About />} />
</Routes>
</React.Suspense>
</BrowserRouter>
);
}
Common Issues and Solutions
Let's address some common issues developers face when working with MUI Links.
Link Not Working with React Router
Problem: Links are causing full page reloads instead of client-side navigation.
Solution: Make sure you're using the component
prop correctly:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
// Incorrect - will cause full page reload
function IncorrectLink() {
return (
<Link href="/about">About</Link>
);
}
// Correct - uses React Router for client-side navigation
function CorrectLink() {
return (
<Link component={RouterLink} to="/about">About</Link>
);
}
// Alternative correct approach
function AlternativeCorrectLink() {
return (
<RouterLink to="/about" style={{ textDecoration: 'none' }}>
<Link component="span">About</Link>
</RouterLink>
);
}
Styling Not Applied Correctly
Problem: Custom styles are not being applied to the Link component.
Solution: Check your style application method and specificity:
import * as React from 'react';
import Link from '@mui/material/Link';
import { styled } from '@mui/material/styles';
// Problem: Styles not specific enough
const ProblemLink = styled(Link)({
color: 'red', // This might be overridden by theme
});
// Solution 1: Use higher specificity
const Solution1Link = styled(Link)(({ theme }) => ({
'&.MuiLink-root': {
color: 'red',
},
}));
// Solution 2: Use the sx prop for one-off styling
function Solution2Link() {
return (
<Link
href="#"
sx={{
color: 'red',
'&:hover': { color: 'darkred' }
}}
>
Red Link
</Link>
);
}
// Solution 3: Override at theme level for consistent styling
// In your theme configuration:
const theme = createTheme({
components: {
MuiLink: {
styleOverrides: {
root: {
color: 'red',
'&:hover': {
color: 'darkred',
},
},
},
},
},
});
Accessibility Issues
Problem: Links are not accessible to all users.
Solution: Ensure proper ARIA attributes and keyboard navigation:
import * as React from 'react';
import Link from '@mui/material/Link';
// Problem: Icon-only link without accessible label
function ProblemIconLink() {
return (
<Link href="/settings">
<SettingsIcon />
</Link>
);
}
// Solution: Add proper aria attributes
function AccessibleIconLink() {
return (
<Link
href="/settings"
aria-label="Settings"
sx={{
display: 'flex',
padding: '8px',
borderRadius: '4px',
'&:focus-visible': {
outline: '2px solid blue',
outlineOffset: '2px',
}
}}
>
<SettingsIcon />
</Link>
);
}
Best Practices for MUI Links
After years of working with MUI Links, I've developed these best practices:
1. Be Consistent with Link Styling
Maintain visual consistency by using theme customization:
import * as React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import Link from '@mui/material/Link';
const theme = createTheme({
components: {
MuiLink: {
defaultProps: {
underline: 'hover',
},
styleOverrides: {
root: {
fontWeight: 500,
'&:hover': {
color: '#1565c0', // Consistent hover color
},
},
},
},
},
});
function ConsistentLinks() {
return (
<ThemeProvider theme={theme}>
<Link href="/page1">Page 1</Link>
<Link href="/page2">Page 2</Link>
<Link href="/page3">Page 3</Link>
</ThemeProvider>
);
}
2. Create Purpose-Specific Link Components
For different use cases, create specialized link components:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import { styled } from '@mui/material/styles';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
// Internal navigation link
const NavLink = styled(Link)(({ theme }) => ({
textDecoration: 'none',
padding: '8px 16px',
borderRadius: theme.shape.borderRadius,
'&:hover': {
backgroundColor: theme.palette.action.hover,
},
}));
// External link with icon
function ExternalLink({ href, children, ...props }) {
return (
<Link
href={href}
target="_blank"
rel="noopener noreferrer"
sx={{
display: 'inline-flex',
alignItems: 'center',
gap: 0.5,
}}
{...props}
>
{children}
<OpenInNewIcon fontSize="small" />
</Link>
);
}
// Usage
function App() {
return (
<div>
<NavLink component={RouterLink} to="/dashboard">Dashboard</NavLink>
<ExternalLink href="https://mui.com">MUI Documentation</ExternalLink>
</div>
);
}
3. Handle Link State Management
For complex navigation, manage link states properly:
import * as React from 'react';
import { useLocation, Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
function StateAwareNavigation() {
const location = useLocation();
// Helper to check if a path is active
const isActive = (path) => {
// Exact match for home page
if (path === '/' && location.pathname === '/') {
return true;
}
// For other pages, check if the current path starts with the link path
// This handles nested routes
return path !== '/' && location.pathname.startsWith(path);
};
return (
<Box sx={{ display: 'flex', gap: 2 }}>
<Link
component={RouterLink}
to="/"
sx={{
fontWeight: isActive('/') ? 'bold' : 'normal',
color: isActive('/') ? 'primary.main' : 'text.primary',
}}
>
Home
</Link>
<Link
component={RouterLink}
to="/products"
sx={{
fontWeight: isActive('/products') ? 'bold' : 'normal',
color: isActive('/products') ? 'primary.main' : 'text.primary',
}}
>
Products
</Link>
<Link
component={RouterLink}
to="/services"
sx={{
fontWeight: isActive('/services') ? 'bold' : 'normal',
color: isActive('/services') ? 'primary.main' : 'text.primary',
}}
>
Services
</Link>
</Box>
);
}
4. Optimize for Mobile and Touch Devices
Make links touch-friendly for mobile users:
import * as React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
import { styled } from '@mui/material/styles';
// Touch-friendly link
const TouchLink = styled(Link)(({ theme }) => ({
display: 'inline-block',
padding: '12px 16px', // Larger touch target
[theme.breakpoints.down('sm')]: {
padding: '16px', // Even larger on mobile
fontSize: '1.1rem',
},
}));
function MobileNavigation() {
return (
<nav>
<TouchLink component={RouterLink} to="/">Home</TouchLink>
<TouchLink component={RouterLink} to="/products">Products</TouchLink>
<TouchLink component={RouterLink} to="/contact">Contact</TouchLink>
</nav>
);
}
Wrapping Up
The MUI Link component is a versatile tool for building navigation systems in React applications. By understanding its core features, customization options, and integration patterns, you can create intuitive, accessible, and visually appealing navigation experiences for your users.
Whether you're building a simple website or a complex application, proper use of links is essential for guiding users through your interface. The techniques and patterns covered in this article should give you a solid foundation for implementing effective navigation with MUI Links.
Remember that good navigation is about more than just functionality—it's about creating a seamless experience that helps users achieve their goals efficiently. By applying these principles and practices, you'll be well on your way to building navigation systems that enhance your application's usability and user satisfaction.