Skip to main content

Command Palette

Search for a command to run...

JavaScript Links and AI Crawlers: Make Navigation Discoverable

A focused audit of anchors, click handlers and routes that work only after the app has loaded.

Updated
•5 min read•View as Markdown

A pricing card can work perfectly when you click it and still expose no link to the pricing page.

That is the navigation problem I would check before changing how the whole app renders. The useful question is simple: where is the destination represented?

My earlier raw HTML versus rendered HTML article covered the different documents a crawler might receive. This article stays with one part of that problem: whether important pages are connected by usable links.

Inspect the HTML behind the component

A component called ServiceCard might produce this:

<button onClick={() => navigate('/services/maintenance')}>
  Website maintenance
</button>

The button can run JavaScript and change the route. The markup does not expose that destination as a link.

For navigation, the relevant output is:

<a href="/services/maintenance">Website maintenance</a>

You can use your router's link component to preserve client-side navigation. Inspect the HTML it actually produces. The component name alone doesn't prove that an anchor and a usable href are present.

Keep buttons for actions such as opening a dialog or applying a filter. Use a link when the intent is to take someone to another page.

Google's crawlable-link guidance specifically recommends anchors with resolvable href values. It also explains that JavaScript-inserted links can be crawlable when they produce that markup. This is documented Google behavior, not a guarantee about every AI crawler.

Check the destination before testing the click

An anchor is only useful if its href identifies the intended destination.

Look for empty values, a bare #, javascript: URLs or handlers that ignore the href and send the visitor somewhere else. A card can look like navigation while its only useful routing information lives inside an event handler.

For the maintenance example, I would expect to find /services/maintenance in the anchor. Then I would copy that address and open it directly in a fresh tab.

If the route works after entering through the homepage but fails when opened directly, investigate the production host and route handling. A successful in-app transition has hidden a separate delivery problem.

Check the returned page as well. A 200 response containing the homepage is not evidence that the maintenance route works. The destination needs its own relevant heading and content.

There are three useful checkpoints for the page containing the link:

  1. The initial HTML response.

  2. The rendered DOM before any interaction.

  3. The DOM after opening a menu or clicking a control.

Search for the actual anchor and its href at each stage. A destination appearing inside a script string or JSON object is not the same as a navigation link.

If the anchor is already in the initial response, you have established that it is available without running the app. If rendering adds it, its availability depends on the reader's rendering path. If it only appears after a click, discovery depends on that additional interaction too.

This is a useful distinction for menus. A collapsed menu may already contain anchors in the document. Another menu may fetch and create them only when opened. They can look identical in the interface while presenting different navigation paths to an automated reader.

Do not treat your browser test as proof of a particular AI crawler's behavior. It establishes what your page delivered in that test. Check the relevant service's documentation and available crawl evidence before making a crawler-specific claim.

Audit a path through the site

Checking the homepage alone leaves too much untested. Follow a real path a visitor needs, such as product page to integration guide to setup instructions.

For each step, record the source page, the anchor's href and whether the destination loads directly. Also record whether the anchor appears before rendering, after rendering or only after interaction.

That gives you a useful bug report: "The product card reaches the integration guide in-app, but it is a button with no anchor. The destination itself loads correctly." The repair can stay in the card component.

A different finding needs a different fix: "The guide has a real link, but a direct request to the setup route returns the homepage." Rewriting the anchor text won't repair that route.

The link graph matters too. A detailed guide can exist without a useful link from the page that explains the related product. Creating another guide would leave that missing connection in place. Add the relevant path and verify it.

Verify the shared component in more than one place

After a repair, check the header, cards and related-article navigation separately. They may look similar while using different components. Read the link text without the design around it. It should help someone understand where they are going.

Vite itself does not decide whether your navigation becomes anchors, buttons or a rendered server document. Diagnose the deployed output and the route behavior.

I would call this audit complete when the important paths expose real destinations, those routes load independently, and each destination contains the intended page. A rendering layer may help with a measured delivery gap, but it cannot infer a missing navigation relationship that the application never creates.

This article was adapted from my earlier technical writing with AI assistance.

M
Mohan17h ago

The "button vs anchor" point is so easy to miss. Everything works when you click, so you never think to check. I run a React + Vite SPA on Netlify, and the "open the route directly in a fresh tab" check is a good one. A 200 that's secretly the homepage looks fine in every in-app test. I'd add one thing to the audit: check that each route serves its own title and meta tags, not just its own content. Did you find AI crawlers behaving differently from Googlebot on links that only exist after rendering?

More from this blog

P

Prerender Buddy

2 posts

Practical notes on prerendering, crawler visibility, AI search, technical SEO, and building websites that search engines and AI systems can actually discover.