Using <a> Tags Instead of <Link> in React Router
A common React Router mistake is using regular HTML <a> tags for navigation, which causes full page reloads instead of the expected client-side routing. This defeats React’s single-page application (SPA) behavior and hurts performance.
The Problem
// ❌ Wrong - Causes full page reload
function Navigation() {
return (
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
);
}
Why this is problematic:
- Triggers full browser page reload
- Loses React application state
- Slower navigation experience
- Defeats SPA benefits
- Causes unnecessary server requests
Correct Solutions
1. Basic Link Usage (Recommended)
import { Link } from 'react-router-dom';
function Navigation() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
);
}
2. Active Link Styling
import { NavLink } from 'react-router-dom';
function Navigation() {
return (
<nav>
<NavLink
to="/"
className={({ isActive }) => isActive ? 'active' : ''}
>
Home
</NavLink>
<NavLink to="/about">About</NavLink>
</nav>
);
}
3. Programmatic Navigation
import { useNavigate } from 'react-router-dom';
function ProductCard({ id }) {
const navigate = useNavigate();
return (
<div onClick={() => navigate(`/products/${id}`)}>
{/* Product content */}
</div>
);
}
Key Differences
| Feature | <a> Tag | <Link> Component |
|---|---|---|
| Navigation | Full page reload | Client-side routing |
| Performance | Slower (new HTTP request) | Instant (no reload) |
| State | Lost | Preserved |
| Browser History | New entry | Controlled via router |
| Props | href | to |
When to Actually Use <a> Tags
- External links (to other websites):
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
External Site
</a>
- File downloads:
<a href="/files/report.pdf" download>Download Report</a>
- Email links:
<a href="mailto:contact@example.com">Email Us</a>
Common Mistakes to Avoid
- Mixing href and to:
<Link href="/about">About</Link> // ❌ Wrong prop
- Forgetting link styling:
// Add CSS to remove default styling
a, Link {
text-decoration: none;
color: inherit;
}
- Nested click handlers:
<Link to="/about" onClick={handleClick}>
About {/* ❌ Both navigation and click handler may conflict */}
</Link>
Best Practices
- Always use
<Link>for internal navigation - Use
<NavLink>for navigation with active state - Style consistently between
<a>and<Link> - Add rel attributes for external links:
<a href="..." target="_blank" rel="noopener noreferrer">
- Consider abstracting your link components:
function AppLink({ to, children, ...props }) {
const isInternal = /^\/(?!\/)/.test(to);
return isInternal ? (
<Link to={to} {...props}>{children}</Link>
) : (
<a href={to} target="_blank" rel="noopener noreferrer" {...props}>
{children}
</a>
);
}
Advanced Patterns
1. Type-Safe Links (TypeScript)
type RoutePath = '/' | '/about' | `/products/${string}`;
interface AppLinkProps extends LinkProps {
to: RoutePath;
}
function AppLink({ to, ...props }: AppLinkProps) {
return <Link to={to} {...props} />;
}
2. Custom Link Component
function CustomLink({ to, children }) {
const path = useResolvedPath(to);
const match = useMatch({ path: path.pathname, end: true });
return (
<Link
to={to}
style={{ color: match ? 'red' : 'blue' }}
>
{children}
</Link>
);
}
3. Analytics Integration
function TrackedLink({ to, event, ...props }) {
const track = useAnalytics();
return (
<Link
to={to}
onClick={() => track('link_click', { event })}
{...props}
/>
);
}
Remember: The <Link> component is one of React Router’s most important features. It:
- Preserves your SPA behavior
- Maintains application state during navigation
- Provides faster transitions between views
- Integrates with React Router’s advanced features
Always prefer <Link> over <a> for any navigation within your React application, and reserve <a> tags only for external resources or special cases like file downloads.
