scalpel@labs: ~/glossary/javascript-link.mdx6 sections

JavaScript Links

A JavaScript link is an anchor tag whose `href` is a `javascript:` URI (or missing entirely, relying on `onclick`), rather than pointing to an actual URL. Because there's no `href` for crawlers to extract, Googlebot has nothing to add to its discovery queue.

extension: Scalpel SEOupdated: 2026-08-14read_time: 2 min
less javascript-link.mdx

Orphaned pages get no traffic

A page reachable only via javascript: links or onclick handlers is invisible to Googlebot's link-discovery process. Entire site sections can vanish from search this way, even if the pages render perfectly for clicking humans.

The fix is simple and standard: give every link a real href pointing to the actual URL, then layer JavaScript on top with event.preventDefault() if you need client-side routing. This works for crawlers, keyboard users, and users with JavaScript disabled. One href serves all three audiences.

What crawlers see

Googlebot extracts href attributes from anchor elements to find new pages. When an anchor has href="javascript:void(0)" or only an onclick handler, there's no href to extract: the crawler has nothing to follow.

<!-- No href for crawlers to extract -->
<a href="javascript:void(0)" onclick="navigate('/page')">Click me</a>

<!-- Crawler can follow /page, JS can prevent full reload -->
<a href="/page" onclick="event.preventDefault(); navigate('/page')">Click me</a>

The second pattern lets browsers and crawlers follow the real href. JavaScript intercepts the click and does client-side routing, but the href stays intact for crawlers that don't execute JavaScript.

Not a penalty, just an orphaning problem

javascript: links aren't a direct ranking penalty, but the orphaning effect is a real crawlability issue. The problem is discovery, not a search-engine rule against the pattern.

Modern frameworks like Next.js and React Router handle this by default: they output real href attributes and handle client-side routing transparently. If you're building from scratch, you have no excuse not to follow the same pattern. It's standard practice now.

Build it right: real href + JS enhancement

Bad, no crawlable href:

<nav>
  <a href="javascript:void(0)" onclick="navigateTo('/about')">About</a>
  <a href="javascript:void(0)" onclick="navigateTo('/contact')">Contact</a>
</nav>

Good, crawlable href, JS enhancement on top:

<nav>
  <a href="/about" onclick="event.preventDefault(); navigateTo('/about')">About</a>
  <a href="/contact" onclick="event.preventDefault(); navigateTo('/contact')">Contact</a>
</nav>

<script>
function navigateTo(path) {
  history.pushState({}, '', path);
  loadPageContent(path);
  // Crawlers follow the real href; JS handles smooth transitions
}
</script>

How Scalpel flags it

Scalpel counts javascript: links separately in the Links panel. It flags pages that might be orphaned: reachable only via JavaScript navigation, a sign to audit your site structure and add real href attributes.

For single-page apps

Build SPAs with a framework that outputs real href attributes in the HTML (Next.js Link, Remix, SvelteKit). Then implement client-side routing to intercept navigation. Crawlers follow the real hrefs and users get smooth transitions without a full page reload.

Sources