SEO for Apps: I Audited 11 Vibe-Coded Products and Claude Couldn't Read One

6 months, 11 audits, the same 5 mistakes. Your builder reports a 90 SEO score while ClaudeBot gets an empty div.

10 min read

Your app is finally live. Now how do your customers find it, inside Claude or inside Google?

You spent 40 hours prompting Claude or Codex to build the thing. It runs, it looks good, it is online. And Claude cannot read it.

Not figuratively. ClaudeBot downloads your JavaScript bundle in almost 24% of its requests and never executes it. It grabs the file, drops it, moves on. Anthropic documents this itself in the web fetch tool docs: dynamically rendered JavaScript sites are not supported, the model only gets the initial HTML served by the server. On a standard vibe-coded app, that initial HTML is an empty div and a <script src>.

I audited 11 products over the last 6 months. SaaS in early access, side projects with real users, a couple of things already making money. The same 5 mistakes every time. And every time the builder dashboard was showing an SEO score above 90, with the nice little green pill lol.

Office worker proudly pointing at 90% SEO score while superhero behind holds phone with blank loading screen; robotic cat tangled in cables nearby
Your SEO score is great. Your app? Invisible to crawlers.

Run This Test Before You Read Further

Right click on your homepage, then View Page Source. Not the inspector. The inspector shows you the DOM after your JavaScript ran, which is the view guaranteed to reassure you. View Page Source shows the raw response from the server, and that is what a crawler receives.

Now Ctrl+F inside that source. Look for a sentence you can actually see on the page: your headline, your pricing line, the name of your main feature. If the sentence is not in there, your diagnosis is already done.

Second test, 10 seconds of setup. Install the Web Developer extension, disable JavaScript, reload the page. Whatever stays on screen is roughly what an AI crawler works with. On most vibe-coded apps what stays is a white page, sometimes with a loading spinner that will keep spinning until the heat death of the universe.

Third test, nothing to install. Search Google for an exact sentence from your page, in quotes. No result means your text is not in the index under those words.

11 Audits, 6 Months, the Same 5 Mistakes

The stacks are never the same twice. Lovable, Bolt, v0, plain React shipped on Vercel. The list of problems does not move.

  1. The content does not exist in the raw HTML. Everything gets injected client side once the bundle loads.
  2. The heading structure is broken. No h1 at all, or 4 of them on the same route, or a jump straight from h1 to h4 because that was the font size that looked right.
  3. Meta title and description are missing, or strictly identical on every route. 40 pages, 1 title.
  4. Navigation runs through buttons with a programmatic redirect instead of real anchors. Nothing to follow, so internal linking does not exist.
  5. robots.txt and sitemap.xml are the generated defaults. They point at a domain that is not yours, or at a sitemap that answers 404.

These are not exotic problems. It works like the yearly technical inspection on a car, where it is always the brake pads and the lights and almost never something you have never seen before. The reason it repeats is not incompetence. No step of the build flow ever fails on any of these 5 points. Everything stays green, the app deploys, the demo works, and the part that decides whether anyone finds you was never tested by anything.

The Dashboard Says 90. The Crawler Says Nothing.

A builder published the audit of their own Lovable product and the numbers are the cleanest illustration of the gap I have seen. Searching the public files for the button with programmatic navigation pattern returned 20 occurrences across 11 files, including the main CTA of the homepage and every product link. Googlebot was receiving an empty HTML page with a bundle attached. The Lovable SEO panel was reporting a score above 90 the entire time.

The score is not lying, by the way. It measures the page after the browser did its work, and measured that way the page really is fine. It works on my machine, dashboard edition 🤷

Second case, different tool, same shape. A Bento Grid generator, vibe-coded in 2 days, launched on Product Hunt and picked up quickly. Then John Mueller showed up in the Reddit thread and listed what he found: the key content sitting in a JavaScript file the crawler does not read, meta tags deprecated years ago, broken hreflang, structured data that did not match the page.

The product was good. The layer that makes a product findable was simply not generated by default, and no dashboard was going to say so.

Google Indexes You. The Answer Engines Don't.

Googlebot renders JavaScript, and has done so for years. So your app does get indexed, you see impressions in Search Console, and you conclude the whole subject is handled.

It is handled at a price. Research from Onely, relayed in February 2026 inside a technical analysis by Jan-Willem Bobbink, puts the indexing of JS-heavy pages at roughly 9 times slower than static HTML, with sites that exceed their render budget losing up to 40% of their indexation rate. Rendering is a queue and you are standing at the back of it.

The real gap sits somewhere else. As of June 2026, no major AI crawler renders JavaScript: GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, PerplexityBot, Meta-ExternalAgent, Bytespider. They fetch, they do not execute. The only significant exception is Gemini, which inherits the Googlebot rendering infrastructure. Then add Bing, since a large share of ChatGPT agent queries leans on the Bing index, and Bing JavaScript rendering is limited.

Reading raw server logs is the last part of this job that still feels like archaeology. Half the user-agents hitting a small site belong to bots I could not identify at all, scrapers named after animals, academic crawlers from universities that stopped funding the project around 2019 and never turned the machine off. A whole ecosystem of dead software, still politely requesting your sitemap every 6 hours.

I once asked 3 answer engines to recommend my SaaS and the only one that could name it was the one that had something to read.

You can rank on Google and exist in 0 answer engines.

2 Readers, 2 Pages

A browser and a crawler do not read the same page, and once that clicks, the 5 mistakes stop looking like 5 problems and start looking like 5 symptoms of the same one. The browser downloads your HTML, finds a nearly empty shell, fetches the bundle, executes it, builds the DOM, paints the result and hands you a finished page you can screenshot for Product Hunt. A retrieval crawler downloads that same HTML, reads it, and stops there. There is no render queue behind it and nothing comes back later to check whether the page filled itself in. The SEO scores built into your builder measure the first state because that is the state a human sees, and every answer engine reads the second state because that is the only one it is willing to pay for. The disagreement between your green dashboard and your empty analytics fits entirely inside the gap between those 2 pages.

The corollary decides the order of the repairs, and it is the part I did not expect when I started doing these audits. When the raw HTML contains nothing, the only information that exists about your product lives somewhere else: your business listing, your profiles, a changelog hosted on a third party site, a directory entry, a Reddit comment. That is the material a model has to work with when somebody asks it for a tool like yours. Which means the entity work, boring as it is, carries the same weight as the technical work. Fixing the HTML makes you readable, and the mentions elsewhere are what a model has to go on in the meantime.

3 Fixes You Can Ship Today

Fix 1. Put the words inside the HTML you serve.

Right now the crawler receives something close to this:

<div id="root"></div>
<script type="module" src="/assets/index-a3f9.js"></script>

There are 2 ways out. The per-route solution injects a real title, a real meta description and a minimum of text content into the response for each route, which is what you want if your app has pages that differ. The global solution injects a single set of tags for the whole app, which is faster and still better than nothing when everything you sell lives on the homepage. Either way, the test is the same as before: kill JavaScript, reload, and check that words are still on the screen.

The tags themselves deserve 5 minutes rather than a copy of your app name. A title written to win a Google ranking and a title written to be quoted inside an answer are not the same string, and that gap is why title tags decide AI search visibility.

Fix 2. A single h1 per route, and real links.

Continuous hierarchy, no jumps, 1 heading that says what the page is. Then replace the fake navigation:

// invisible to a crawler, there is nothing to follow
<button onClick={() => navigate("/pricing")}>Pricing</button>

// followable, and your router still handles it
<Link to="/pricing">Pricing</Link>

This single change is what makes internal linking exist on your site, because until you do it there is no path between your routes for anything to walk.

Fix 3. The 2 generated files that point nowhere.

Open robots.txt. If you find this, your sitemap has never been read by anything:

Sitemap: https://example.com/sitemap.xml

Check the sitemap actually answers, check the URLs inside it are your real routes, and check that you are not blocking the AI user-agents you want reading you. Blocking GPTBot and then wondering why ChatGPT never mentions your product is a conversation I have had more than once.

In a first time, ship these 3 and nothing else. Then run PageSpeed Insights and aim for something around 90 on mobile, keeping in mind that this score grades the human experience and will happily give you a great number on a page that no crawler can read.

What's Left After the 3 Fixes

2 mistakes stay on the table and I left them out on purpose, because neither is an afternoon of work.

The first is the rendering decision itself. Prerendering serves a static snapshot to crawlers and buys you visibility this week. A server-side migration rebuilds the app so that every visitor, human or bot, receives the same complete document. The snapshot approach also carries a real risk: serve a different version to LLM user-agents with an approximate implementation and you are a short step from cloaking. I think the render gap stays open for a while, since rendering costs money per page and these bots crawl at a scale where that number gets ugly fast, but I would not bet a product on that reading being right.

The second is entity work, and it happens outside your repository. Business listing, consistent profiles across platforms, mentions on sites you do not own, coherent naming everywhere. Weeks of small boring actions with no deploy button at the end, which makes it the XP grind of this whole discipline. I packaged the audit prompts, the route-by-route templates and the full entity sequence into a kit, for people who would rather not rebuild that checklist from zero.

An agency will bill you around 1500 dollars to tell you exactly that. It will be right on the substance and wrong on the order, because the SSR migration is the complete fix, and on a product with no traffic to protect yet you are repairing a highway nobody drives on.

Building an app costs 20 dollars a month and a weekend. Getting found still costs the old price. C'est la vie.

Sources

This post may contain affiliate links. If you click them, I might earn a small commission (costs you nothing, and helps me keep shipping quality articles every day for your reading pleasure).


Most vibe-coded apps fail the same 5 SEO tests, but your builder dashboard keeps showing green. The demo-vs-product checklist in the welcome kit includes production visibility rules that catch this before your app goes live invisible to Claude and Google.

→ Get the welcome kit