Why your React site might be invisible to Google
Sites built with React, Vue or similar frameworks can look empty to search engines and link previews. How to check yours, and the fixes that work.
Your new website looks great in the browser. It's fast, it's modern, and it was built with React (or Vue, Svelte or Angular). Then weeks go by and it barely appears in search results. When you share a link on social media or a messaging app, the preview shows a generic title, or nothing at all.
A common cause is client-side rendering: the server sends an almost empty HTML page, and JavaScript builds everything you see after it loads in the browser. People with a modern browser rarely notice. Many crawlers do.
This guide explains what's going on, how to check your own site in a few minutes, and the fixes that work.
What the server actually sends
When a browser or a crawler asks for a page, the server replies with HTML. On a traditional website, that HTML already contains the headings, text, links and meta tags.
On a client-side rendered app, created with many popular starter templates, the HTML often looks something like this:
<!doctype html>
<html>
<head>
<title>My App</title>
<script type="module" src="/assets/index-4f2a.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
That's the whole page as delivered. There's no business name, no services, no text, no links to other pages. Everything appears only after the browser downloads and runs the JavaScript file.
How Google handles this
Google can run JavaScript. Its documentation describes three stages: crawling, rendering and indexing. Googlebot fetches the HTML first, then queues the page for rendering, where a headless version of Chrome runs the JavaScript. Google says the page may wait in that queue for a few seconds, but it can take longer, and it gives no fixed time.
So a JavaScript-only page can be indexed, but it depends on an extra step, and plenty can go wrong along the way:
- Rendering can fail. A script error, a blocked file in robots.txt, or an API call that times out can leave Google with the same empty shell.
- Links may not be found. If navigation uses click handlers instead of real
<a href="...">links, crawlers may never discover your other pages. - Hash URLs don't work as separate pages. Routes like
example.com/#/servicesare treated as the same URL as the home page. Google's guidance is to use normal paths with the History API instead. - Error pages can look like real pages. A single-page app often returns a
200 OKstatus for every URL, including ones that don't exist, then shows "not found" with JavaScript. Search engines can end up indexing those error pages.
Google's own advice is that server-side rendering or pre-rendering is still a good idea, because it makes pages faster for users and crawlers, and not all bots can run JavaScript.
Not every crawler is Google
That last point matters more than it used to. Many of the services that read your pages don't run JavaScript at all, or only in limited ways. That includes link-preview bots used by social networks and messaging apps, many SEO and monitoring tools, and some other search engines and AI crawlers. Each of these services documents its own behaviour, and it changes over time, so the safest assumption is simple: if it's not in the HTML, some important visitors won't see it.
A very common symptom is link previews. If your page title, description and Open Graph image are set by JavaScript after load, a preview bot that reads only the raw HTML will see the default title from the template, often "React App" or "Vite + React", and no image.
How to check your own site in five minutes
1. View the source, not the inspector
Open a page, then press Ctrl+U (or Cmd+Option+U on a Mac) to view the page source. This shows the HTML the server sent, before JavaScript ran. Use your browser's find (Ctrl+F) to search for a sentence you can see on the page.
If the sentence isn't in the source, that content is built by JavaScript. The browser's Inspect panel won't tell you this, because it shows the page after JavaScript has run.
2. Check the title and meta tags in the source
In the same source view, look at the <title>, <meta name="description"> and og: tags. Are they specific to the page, or the same placeholder on every page?
3. Use Google Search Console
If you have Search Console set up, the URL Inspection tool shows whether a page is indexed. Test live URL followed by View tested page shows the HTML Google rendered and a screenshot. If the screenshot is blank or the rendered HTML lacks your content, Google is having trouble too.
4. Try a URL that shouldn't exist
Visit something like yourdomain.com/this-page-does-not-exist and check the HTTP status code in your browser's developer tools (Network tab). It should be 404. If it's 200, you have "soft 404" pages that search engines may index.
5. Run a crawler-style scan
An SEO scan that reads the raw HTML sees roughly what a non-rendering crawler sees. If it reports that a content-rich page has almost no visible text, that's a strong sign the content only exists after JavaScript runs.
The fixes, from most to least robust
Server-side rendering (SSR)
The server runs your React code for each request and sends complete HTML. JavaScript then takes over in the browser to make the page interactive. Frameworks built on React, such as Next.js and React Router in framework mode (the successor to Remix), support this. Nuxt does the same for Vue, and SvelteKit for Svelte.
This is the most complete fix for sites whose content changes often or depends on the visitor, but it means running a server, not just hosting static files.
Static site generation (SSG) or pre-rendering at build time
Each page is rendered to a complete HTML file when you build the site, and those files are served as normal. Most of the frameworks above can do this, as can Astro and others. For a typical business site, where pages change only when someone edits them, this is often the best balance: complete HTML, fast loading and simple hosting.
Pre-rendering services
Some services sit in front of your app and serve a rendered snapshot to crawlers while visitors get the normal app. This can be a useful stopgap when rebuilding isn't possible yet, but it adds another moving part, and the snapshot must match what users see. Treat it as temporary.
Quick wins whatever you choose
Even before a bigger change, a few fixes help:
- Put a meaningful
<title>, meta description and Open Graph tags into the HTML of each page, not just the home page. - Use real
<a href="/services">links for navigation, not buttons with click handlers. - Use clean paths (
/services), not hash routes (/#/services). - Return a real
404status for missing pages. - Make sure robots.txt doesn't block your JavaScript, CSS or API files that are needed to render the page.
- Keep a sitemap listing every page you want indexed, and submit it in Search Console.
When to worry, and when not to
Not every React site has this problem. Plenty of sites built with Next.js, Astro or similar tools already send complete HTML. And some parts of a site don't need to be indexed at all, such as a logged-in dashboard or a checkout.
The pages to check are the ones you want people to find through search: your home page, service or product pages, location pages, articles and contact page. If those arrive as an empty <div id="root"></div>, fixing it is likely to be one of the most valuable SEO changes you can make. For a structured way to work through the rest of your findings, see How to read an SEO audit.
The short version
Press Ctrl+U on your important pages and search for your own text. If it isn't in the source, crawlers that don't run JavaScript can't see it, and even Google has to take an extra rendering step. Move those pages to server-side rendering or static generation, put real titles and meta tags in the HTML, use real links and paths, and return proper 404s.