oxc-project/oxc · warning · OxcDiagnostic

Do not use `<a>` elements to navigate between Next.js pages.

Error message

Do not use `<a>` elements to navigate between Next.js pages.

What it means

Warning from oxlint rule `nextjs/no-html-link-for-pages`. Internal links rendered as plain `<a href="/...">` force a full document reload, losing client-side navigation, prefetching, and scroll restoration. The rule flags anchor tags whose string `href` targets an internal route.

Source

Thrown at crates/oxc_linter/src/rules/nextjs/no_html_link_for_pages.rs:12

use oxc_ast::{
    AstKind,
    ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

use crate::{AstNode, context::LintContext, rule::Rule};

fn no_html_link_for_pages_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Do not use `<a>` elements to navigate between Next.js pages.")
        .with_help("Use `<Link />` from `next/link` instead for internal navigation. See https://nextjs.org/docs/messages/no-html-link-for-pages")
        .with_label(span.label("Replace with `<Link>` from `next/link`"))
}

#[derive(Debug, Default, Clone)]
pub struct NoHtmlLinkForPages;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Prevents the usage of `<a>` elements to navigate between Next.js pages.
    ///
    /// ### Why is this bad?
    ///
    /// Using `<a>` elements for internal navigation in Next.js applications can cause:
    /// - Full page reloads instead of client-side navigation
    /// - Loss of application state
    /// - Slower navigation performance

View on GitHub (pinned to e1e7af627c)

Solutions

  1. `import Link from 'next/link'` and use `<Link href="/about">About</Link>`.
  2. Keep `<a>` only for external URLs (full `https://...`) or non-navigation anchors (`#`, mailto, downloads).

Example fix

// before
<a href="/about">About</a>

// after
import Link from 'next/link';
<Link href="/about">About</Link>
Defensive patterns

Strategy: validation

Validate before calling

function isInternalHref(href: string): boolean {
  return href.startsWith('/') && !href.startsWith('//');
}
// review flagged: rg -n '<a [^>]*href="/"?' -g '*.tsx' src

Type guard

function isInternalHref(href: string): boolean {
  return href.startsWith('/') && !href.startsWith('//');
}

Prevention

When it happens

Trigger: A JSX `<a>` element whose `href` is a string literal starting with `/` (no scheme or host), i.e. an app-internal URL.

Common situations: Migrating server-rendered HTML with hard-coded links; linking between routes in landing pages. Note: upstream this rule is deprecated since Next 13 (Link no longer needs an `<a>` child), but plain internal anchors are still worth converting.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/027f0a4c94f876fe. Report an issue: GitHub.