Primary content requires JavaScript
Your main content is rendered by JavaScript, so most agents see an empty page.
An agent fetches your URL the way curl does: one HTTP request, no browser, no JS engine. If the response body is mostly empty divs that get filled in by client-side React, Vue, or similar, the agent reads the empty version. Your headline, body copy, prices, and structured data are invisible. This is the single most common reason a real, working website scores poorly on agent readiness.
Most AI crawlers (GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot) do not run JavaScript. They fetch the raw HTML and parse what's there. Even agents that can render JS (Googlebot, some advanced crawlers) treat JS-rendered pages with skepticism; they're slower, costlier, and more likely to fail. A page where the H1 is in the rendered HTML wins over a page where the H1 only appears after hydration.
A SPA homepage that returns <div id="root"></div> as its only content is functionally invisible to agent answer engines. Migrating to SSR or SSG typically takes a sprint and pays back permanently: agent citations appear, structured data starts working, and SEO improves as a side-effect. The opportunity cost of waiting is every agent query that picks a competitor with a server-rendered page.
View source (Ctrl-U, not Inspect Element, which shows the post-render DOM). Search for your headline. If you don't find it, your content is JS-dependent. Or run `curl -s https://your-domain.com | grep -i 'your headline'` from a terminal. If grep finds nothing, agents see nothing.
Render the primary content on the server. The choice depends on your stack: server-side rendering (SSR), static-site generation (SSG), or incremental static regeneration (ISR). All three deliver the same fix: the HTTP response contains the actual H1, body text, and structured data. Client-side hydration can still happen for interactivity; the rule is just that the meaningful content is in the initial HTML.
The reference snippet above is platform-agnostic. Below are the concrete steps for the four most common stacks plus a generic fallback for any other host.
Next.js
If you're already on the App Router, server components are the default, so content rendered in a page.tsx without 'use client' ships in the initial HTML. The common failure is wrapping the whole page in 'use client' for one interactive button. Instead, keep the page server-rendered and isolate the interactive bit into a small client component imported at the leaf. For Pages Router, use getStaticProps or getServerSideProps; do not pull data inside useEffect for above-the-fold content.
WordPress
WordPress is server-rendered by default; themes output HTML on the server. JS-dependent content typically creeps in via page builders (Elementor, Divi) that defer rendering, or via React-based block themes. Audit your theme: if you use a builder, switch off "lazy load" / "dynamic render" for above-the-fold sections. If you use a headless setup, switch the front-end framework to SSR/SSG (Next.js, Astro, Eleventy).
Shopify
Liquid templates are server-rendered, and Shopify storefronts ship HTML. The trap is replacing sections with React/Hydrogen apps that render on the client. If you're on standard Shopify, your product pages are already in good shape; verify by view-source on a product URL. If you're on Hydrogen (Shopify's React stack), make sure SSR is enabled (the default) and don't move primary content into useEffect.
Webflow
Webflow exports static HTML at publish time, so content authored in the Designer ships in the initial response. The fix is mostly about making sure dynamic CMS content uses Collection Lists (server-rendered) rather than custom JS that fetches at runtime. If you have third-party embeds (Typeform, Calendly), keep them below the fold so above-the-fold content reads instantly.
Static HTML / any stack
If you control the build, generate HTML at build time. Static-site generators (Astro, Eleventy, Hugo, Jekyll, Next.js export) all produce ready-to-serve HTML. Avoid hand-rolled SPAs (create-react-app, Vite + React without SSR) for content sites; they put your H1 in JavaScript. If you must use a SPA framework, pick one with SSR support (Next.js, Nuxt, SvelteKit, Remix, Astro).
- Marking the entire page 'use client' (Next.js App Router) just to use one interactive component. Keep the page server-rendered and isolate interactivity into leaf client components.
- Loading content via useEffect after mount. Agents do not wait for effects; the initial HTML is empty by the time they've read it.
- Confusing "view source" with "inspect element". Inspect shows the rendered DOM, including JS injection. View source shows the raw HTML the agent sees. Always test with view source.
- Skeleton screens that never get replaced. Some apps render placeholder boxes server-side and fill them in via JS; to an agent that does not run JS, the page is permanently in skeleton state.
- Lazy-loading the H1 / hero copy "for performance." Loading-state UX targets fast networks; agents come from elsewhere and see the loading state. Keep above-the-fold in the initial HTML.
After deploying SSR/SSG, run `curl -s https://your-domain.com | grep -i '<h1'` and confirm your real headline appears. Open view-source in a browser and confirm structured data, body copy, and links are all in the source. Run a fresh AgentSpeed scan, and the 'content js required for primary content' check should pass; the readability score will tick up.
Will SSR slow down my page?
No. SSR adds a small server-side latency cost (typically 30–80ms) but reduces time-to-interactive because the browser has content to paint immediately. Lighthouse and Web Vitals improve, not regress, after a SPA → SSR migration.
Can I keep my React app and just add SSR?
Yes. Next.js, Remix, Astro, and Vite SSR all let you reuse your React components on the server. The migration is mostly tooling: replace create-react-app with one of those frameworks, then move data fetching out of useEffect.
What about prerendering services like Prerender.io?
They work as a stopgap but add latency, cost, and a separate cache to maintain. They also do not always serve the prerendered version to all crawlers (user-agent detection is fragile). Native SSR/SSG is the durable fix.
Free scan, no signup. The score page is permanent and citable, so you can link it from your team's remediation ticket alongside this guide for “Primary content requires JavaScript”.