---
url: /learn/react-vue-angular-agent-readable
title: "React, Vue, Angular: making client-rendered content agent-readable — Siere Learn"
description: "Framework-specific options for giving crawlers a complete response — server rendering, static generation, prerendering and a request-time agent path — with the trade-offs of each and how to verify the result."
type: article
language: en
author: Tomislav Mladenov
canonical: https://www.siere.ai/learn/react-vue-angular-agent-readable
last_modified: 2026-08-18
aeo_generated: 2026-08-24T15:03:59.746Z
---

# React, Vue, Angular: making client-rendered content agent-readable

Framework-specific options for giving crawlers a complete response — server rendering, static generation, prerendering and a request-time agent path — with the trade-offs of each and how to verify the result.

AuthorTomislav Mladenov

Technical reviewerTomislav Mladenov

Last verified2026-08-18

Evidence classDocumented behaviour

Audit dimensionsSource completeness, Structure & identity

Licence[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)

Scope · React 18/19 with Next.js, Vue 3 with Nuxt 3, Angular 17+; framework documentation as of the verification date.

Reading is optional. Running the check is the point.
[See what an AI agent receives from your URL](/tools/agent-response-check)

## The goal, stated precisely

For each route that matters, the HTTP response body — before any script runs — must contain the title, the canonical link, the primary content and the important links. That is the whole requirement. Which framework you use only changes *how* you get there and what it costs.

There are four ways to get there, and they are not mutually exclusive:

Approach

When HTML is produced

Good fit

Watch out for

Server-side rendering (SSR)

On each request

Personalised or frequently changing pages

Server cost, caching strategy, time-to-first-byte

Static generation (SSG)

At build time

Marketing, docs, catalogues that change with deploys

Build time on large sites; stale until rebuilt unless you use incremental regeneration

Prerendering

At build time, for a list of routes, often on top of an SPA

Existing SPAs where a full SSR migration is not on the table

Route list must be maintained; dynamic routes need a source of truth

Request-time agent path

On each request, only for recognized agents or clients asking for `text/markdown`

Sites that cannot change their rendering model, or need a complete structured representation alongside HTML

Must be verified to be equivalent to the human page; identity classification is by header unless verified ([details](/learn/recognized-vs-verified-crawler-identity))

Google describes rendering-on-behalf-of-crawlers ("dynamic rendering") as a [workaround rather than a long-term solution](https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering) and recommends server-side or static rendering where possible. That is good advice for the routes you control. The request-time path remains useful where you do not, or where you want a structured representation that HTML alone does not give.

## React

**Next.js (App Router).** [Server Components](https://nextjs.org/docs/app/getting-started/server-and-client-components) render on the server by default and their output is in the response. Client Components (`"use client"`) are also rendered to HTML on the server for the initial response; what is *not* in the response is anything fetched in `useEffect` or produced after hydration. The practical rule: fetch data in Server Components or route loaders, not in effects, for content that must be in the source.

**Vite + React (SPA).** A default Vite React app returns the shell. Options, roughly in increasing effort: prerender a route list at build time; move to a framework with SSR (Next.js, React Router in framework mode); or add a request-time agent path in front of the existing app.

Check any React route with:

`curl -sL https://example.com/products/123 | grep -c "<h1"
`
If the count is zero and the page has an `<h1>` in the browser, the heading is client-rendered.

## Vue

**Nuxt.** [Universal rendering](https://nuxt.com/docs/guide/concepts/rendering) is the default: the server renders HTML, the client hydrates. `routeRules` let you prerender specific routes at build time or mark others as client-only. **Vue without Nuxt** exposes [`renderToString` from `vue/server-renderer`](https://vuejs.org/guide/scaling-up/ssr.html) for a custom server, and community prerenderers exist for pure SPAs.

The failure to look for in Vue apps is a page whose layout is server-rendered but whose main content is loaded in `onMounted` — the source contains the frame and none of the substance. Count words, not tags.

## Angular

**Angular 17+** ships [`@angular/ssr`](https://angular.dev/guide/ssr): `ng add @angular/ssr` adds a server entry, and later versions add per-route render modes so a project can prerender some routes at build time, server-render others and leave the rest client-only. Older Angular applications without SSR return the shell.

The Angular-specific trap is `HttpClient` calls in `ngOnInit` for the primary content: with SSR enabled they run on the server *and* again on the client unless transfer-state is used, and without SSR they run only in the browser.

## What "done" looks like, per route

Run these before and after the change. The numbers, not the framework, are the acceptance test.

`URL=https://example.com/pricing
UA="Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.4; +https://openai.com/searchbot"

# Visible words in the ordinary source response
curl -sL "$URL" | sed 's/<script[^>]*>.*<\/script>//g; s/<[^>]*>//g' | tr -s ' \n' | wc -w

# Same, with a documented crawler header
curl -sL -A "$UA" "$URL" | sed 's/<script[^>]*>.*<\/script>//g; s/<[^>]*>//g' | tr -s ' \n' | wc -w

# Title and canonical are in the source
curl -sL "$URL" | grep -oiE "<title>[^<]*|rel=\"canonical\"[^>]*"
`
Pass criteria, in the terms the free audit uses:

- **Source completeness** — several hundred visible words in the source response, not a few dozen. The audit marks under 80 characters as failed and under 300 as partial.

- **Structure and identity** — a title and a resolvable canonical URL in the source.

- **Fidelity** — the crawler-header response matches the ordinary one on title, canonical, redirect target and most of its words and links. If you added a request-time agent path, this is where you prove it is equivalent rather than a different page.

## Choosing

- If you can change the rendering model for the routes that matter, do that first: SSR or SSG puts the content in the response for every client and every crawler, documented or not.

- If you cannot — a large legacy SPA, a third-party front end, a team that will not take on SSR — a request-time agent path in front of the app gives recognized agents and `text/markdown` clients a complete representation now, and it can be removed later without touching the app.

- Either way, keep the check above in your deployment pipeline. Rendering regressions are silent in the browser and loud in the source response.

## What this proves

- Each major framework documents a supported way to emit complete HTML on the server or at build time.
- Whether a given route actually does so can be verified from the source response alone.

## What this does not prove

- That server rendering is always the right trade-off for a given application; it has cost, complexity and cache implications.
- That a complete source response guarantees any downstream model outcome.

## Reproduce it yourself

- For each important route, fetch the source response with curl and count visible words (see the JavaScript-websites guide).
- Check that title, canonical link and the primary content are present in the source, not only after hydration.
- Run the free audit on the route and compare Source completeness before and after the change.

## Sources

- [Next.js — Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components) — Vercel, accessed 2026-08-18
- [Vue.js — Server-Side Rendering (SSR)](https://vuejs.org/guide/scaling-up/ssr.html) — Vue.js, accessed 2026-08-18
- [Nuxt — Rendering Modes](https://nuxt.com/docs/guide/concepts/rendering) — Nuxt, accessed 2026-08-18
- [Angular — Server-side rendering](https://angular.dev/guide/ssr) — Google, accessed 2026-08-18
- [Google Search Central — Dynamic rendering as a workaround](https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering) — Google, accessed 2026-08-18

## Change history

- 2026-08-18First published.

## Related guides

- [What AI agents actually receive from JavaScript websites Most AI crawlers fetch the initial HTML and move on. If your page is a client-rendered application, that response can be an empty shell. Here is what is in the response, how to inspect it, and how the free audit measures it.](/learn/what-ai-agents-receive-from-javascript-websites)
- [Markdown endpoints versus SSR, prerendering, and edge middleware Four ways to give an agent a complete, structured response — compared on what they change, what they cost, what they can break, and how you prove each one worked.](/learn/markdown-endpoints-vs-ssr-prerendering-edge-middleware)
- [Is llms.txt enough? Discovery versus delivery llms.txt tells an agent where to look. It does not change what the agent receives when it gets there. This guide separates the two problems and shows how each is checked.](/learn/is-llms-txt-enough)

[← Is llms.txt enough? Discovery versus delivery](/learn/is-llms-txt-enough)[Markdown endpoints versus SSR, prerendering, and edge middleware →](/learn/markdown-endpoints-vs-ssr-prerendering-edge-middleware)

See what a recognized agent receives from your own URL.
[See what an AI agent receives from your URL](/tools/agent-response-check)

## Structured Data

```json
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://www.siere.ai/#organization",
  "name": "Siere",
  "url": "https://www.siere.ai/",
  "logo": "https://www.siere.ai/social-square.png",
  "sameAs": [],
  "contactPoint": {
    "@type": "ContactPoint",
    "contactType": "customer support",
    "email": "contact@siere.ai",
    "url": "https://www.siere.ai/contact"
  }
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "@id": "https://www.siere.ai/#website",
  "name": "Siere",
  "url": "https://www.siere.ai/",
  "description": "Your website is invisible to AI agents. Siere fixes that. We make your site readable, structured, and optimized for the next generation of AI-powered search.",
  "publisher": {
    "@type": "Organization",
    "name": "Siere",
    "@id": "https://www.siere.ai/#organization"
  }
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "SoftwareApplication",
  "@id": "https://www.siere.ai/#software",
  "name": "Agent Experience Optimization",
  "url": "https://www.siere.ai/",
  "description": "Your website is invisible to AI agents. Siere fixes that. We make your site readable, structured, and optimized for the next generation of AI-powered search.",
  "applicationCategory": "BusinessApplication",
  "operatingSystem": "Web",
  "offers": [
    {
      "@type": "Offer",
      "price": "0",
      "priceCurrency": "USD",
      "name": "Starter"
    },
    {
      "@type": "Offer",
      "price": "99",
      "priceCurrency": "USD",
      "name": "Pro"
    }
  ]
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "@id": "https://www.siere.ai/#faq",
  "url": "https://www.siere.ai/",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is Agent Experience Optimization?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "AEO makes your website readable, structured, and reliably citable by AI agents like ChatGPT, Claude, Perplexity, and Gemini. Most modern websites are JavaScript-rendered and look like blank pages to AI crawlers — Siere fixes that without changing your visitor experience."
      }
    },
    {
      "@type": "Question",
      "name": "How does Siere work?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Siere sits between your server and incoming requests. When an AI agent visits, Siere serves an optimized markdown version of your content. When a human visits, your normal page is served unchanged."
      }
    },
    {
      "@type": "Question",
      "name": "Do I need to change my site?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. Siere installs as middleware. There are no content changes or rewrites required."
      }
    }
  ]
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "React, Vue, Angular: making client-rendered content agent-readable",
  "description": "Framework-specific options for giving crawlers a complete response — server rendering, static generation, prerendering and a request-time agent path — with the trade-offs of each and how to verify the result.",
  "url": "https://www.siere.ai/learn/react-vue-angular-agent-readable",
  "mainEntityOfPage": "https://www.siere.ai/learn/react-vue-angular-agent-readable",
  "dateModified": "2026-08-18",
  "author": {
    "@type": "Person",
    "name": "Tomislav Mladenov"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Siere",
    "url": "https://www.siere.ai/"
  },
  "license": "https://creativecommons.org/licenses/by/4.0/",
  "isAccessibleForFree": true,
  "keywords": "React, Next.js, Vue, Nuxt, Angular, SSR, SSG, prerender, hydration",
  "citation": [
    "https://nextjs.org/docs/app/getting-started/server-and-client-components",
    "https://vuejs.org/guide/scaling-up/ssr.html",
    "https://nuxt.com/docs/guide/concepts/rendering",
    "https://angular.dev/guide/ssr",
    "https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering"
  ]
}
```

