JavaScript sites (Next.js, Vue and others): making sure AI crawlers can read your content
Short answer
Googlebot renders JavaScript, but a 2024 study found the major AI crawlers did not. Put the main content, links and metadata in the server HTML using SSR, static generation or pre-rendering. Then verify with a no-JavaScript fetch and server logs.
This guide is for developers and technical leads whose sites are built with React, Next.js, Vue, Nuxt or similar frameworks. It explains why an AI crawler may receive an empty page, which rendering choices fix that, and how to check the result. Auditing a few templates takes an hour. Switching rendering mode on an existing app can take days to weeks.
The short version
Section titled “The short version”- Assume AI crawlers read only the HTML your server returns.
- Find out what that HTML contains for each template.
- Render the main content on the server: SSR, static generation or pre-rendering.
- Put links, metadata and structured data in that HTML too.
- Keep what bots receive the same as what users see.
- Verify with no-JavaScript fetches and crawler logs, and re-check after each release.
1. Assume crawlers read only the server HTML
Section titled “1. Assume crawlers read only the server HTML”Google renders JavaScript. Most AI crawlers, on the best available evidence, don’t.
The study is one observation, now almost two years old, and none of the AI companies document their rendering. The safe design doesn’t depend on who renders: put the content in the HTML. Background: JavaScript rendering and AI crawlers.
2. Check what each template returns
Section titled “2. Check what each template returns”Don’t audit the whole site. Pick one URL per template: home, article, product, category, docs page.
curl -s -A "OAI-SearchBot" https://example.com/page/ > raw.htmlgrep -c "a sentence from the middle of the main text" raw.htmlgrep -iE '<title>|rel="canonical"|name="description"|application/ld\+json' raw.htmlFor each template, record whether the raw HTML has:
| Item | In raw HTML? |
|---|---|
| Main text and headings | |
Internal links as <a href> | |
| Title, description, canonical | |
| Structured data | |
| Content behind tabs, accordions, “load more” |
Then compare with Google’s view in URL Inspection: URL Inspection tool. A gap between the two is what AI crawlers miss.
3. Render the main content on the server
Section titled “3. Render the main content on the server”Modern frameworks already support server rendering. Often the fix is to stop opting out of it.
Choosing a mode:
| Content | Mode |
|---|---|
| Marketing pages, docs, blog | Static generation at build time |
| Catalogue or content that changes often | SSR, or static with revalidation |
| Logged-in app screens | Client rendering is fine. Crawlers don’t need them |
In Next.js, watch for data fetched in useEffect and for "use client" placed high in the tree. In Vue or Nuxt, check that content-critical pages aren’t client-only routes.
So serving a pre-rendered snapshot only to bots is a stopgap for legacy apps. Plan to replace it.
4. Put links and metadata in the HTML too
Section titled “4. Put links and metadata in the HTML too”Crawlers find pages through links and understand them through metadata. Both fail silently when injected late.
- Use real
<a href>links for navigation, not click handlers ondivs: Link best practices. - Render
<title>, meta description, canonical andhreflangon the server: Canonical URL signals, Set up hreflang for translated pages. - Output JSON-LD in the server HTML, not from a tag manager: Structured data and AI search.
- Return real status codes. A client-side “not found” screen with a
200status is a soft 404: HTTP status codes and crawlers. - Keep the XML sitemap generated from the same routes: XML sitemaps.
5. Serve bots the same facts as users
Section titled “5. Serve bots the same facts as users”Server rendering serves the same HTML to everyone, so this comes for free. Bot-only snapshots and user-agent switches are where facts drift: a price, a stock state, a date. If you must keep a snapshot service, compare its output with the live page on a schedule.
6. Verify, then keep verifying
Section titled “6. Verify, then keep verifying”- Check key templates in URL Inspection and confirm they’re indexed.
- Add the
curlchecks from step 2 to CI, so a release that moves content client-side fails the build. - Re-run the template audit after framework upgrades and redesigns.
What not to do
Section titled “What not to do”- Don’t trust a browser test. Your browser runs JavaScript. A crawler may not (step 1).
- Don’t build new sites on dynamic rendering. Google calls it a workaround (step 3).
- Don’t hide key content behind interactions that fetch it on click (step 2).
- Don’t rely on llms.txt or Markdown copies instead of fixing rendering. They aren’t a documented substitute: Claim: llms.txt improves AI search visibility.
- Don’t block crawlers from the JS and CSS that Googlebot needs to render. Google renders with them: robots.txt.
How you’ll know it’s working
Section titled “How you’ll know it’s working”- Raw HTML. Every key template passes the step 2 checklist, in CI.
- Crawler visits. Verified AI search crawlers fetch your pages and get
200: Verify AI crawler traffic in server logs. - Indexing. Pages indexed in Search Console’s page indexing report: Page indexing report.
- Citations. Fixed prompt panels and AI reports, as in Measuring AI visibility by sampling prompts and Bing AI Performance report.
Evidence at a glance
Section titled “Evidence at a glance”| Recommendation | Evidence |
|---|---|
| Googlebot renders JS, in a queue | Official (Google) |
| Major AI crawlers don’t render JS | Observed (Vercel and MERJ, 2024-12) |
| Server-render or pre-render main content | Official (Google, Vue, Next.js) |
| Avoid dynamic rendering for new builds | Official (Google) |
| Same facts for bots and users | Official (Microsoft, for Copilot shopping) |
| Verify with raw fetches and logs | Official (Google) |