---
slug: seo-for-vibe-coded-apps
title: "SEO for Vibe-Coded Apps: Getting Indexed Without Being a Marketer"
excerpt: "SEO vibe coded app guide for solo founders. Why your SPA is invisible to Google and the small handful of fixes that actually matter."
primaryKeyword: "SEO vibe coded app"
publishedAt: 2026-07-08
readingTimeMin: 7
author: "Robert Boylan"
tags:
  - seo
  - indie-dev
  - vibe-coding
  - search
  - marketing
---

You shipped. The app is live. You've posted on X, dropped it in a few Discord servers, maybe got a small spike from Product Hunt. Then the traffic flatlines and you start wondering why nobody finds it on Google.

You type your app name into a search. It shows up. You type a problem your app solves. Nothing.

Here's the uncomfortable part: Google has probably already visited your app and found nothing to index. Not because your content is bad, but because your single-page app (a React or Vue app that renders everything in the browser at runtime) sent Google an almost-empty HTML file, and Google decided there was nothing there worth showing to anyone.

SEO for a vibe-coded app is a specific, fixable problem. Cursor, Lovable, Bolt.new, v0, Replit Agent: they all tend to scaffold the same type of frontend by default. It's great for building fast. It's not great for search indexing out of the box. The fix is smaller than you think, and you don't need a marketing background to apply it.

## Why your SPA shows up as a blank page in Google's cache

A single-page application (SPA for short) is a type of web app where one HTML file loads, and then JavaScript runs to build what the user sees. The browser handles all of that. Google's crawler (Googlebot) also runs JavaScript, but it doesn't always wait for it, and even when it does, the timing is unpredictable.

The result: Google fetches your app, gets back a file that says `<div id="root"></div>` and not much else, and either skips it or gives it a thin-content penalty.

If you go to Google's [URL Inspection Tool](https://search.google.com/search-console/welcome) and paste in one of your public URLs, you can see exactly what Google saw. More often than not, it's a blank white page. That's your SEO problem in one screenshot.

The good news is that this is a structural issue, not a content issue. You don't need to write more words or chase more links. You need to give Google something to read before JavaScript runs.

## Five things every public page needs

Before touching anything architectural, there are five basic tags every page should have. These are so fundamental that they matter even if you solve the rendering problem later. If your AI-built app [skipped the SEO defaults that make a page look professional](/blog/app-looks-ai-generated), this is where you start.

**A title tag.** `<title>Your App Name | What It Does</title>`. Fifty to sixty characters. Specific to the page, not the same string repeated on every route.

**A meta description.** `<meta name="description" content="...">`. One or two sentences (under 160 characters) that describe what this specific page is about. Google shows this in search results. Write it for the person searching, not for algorithms.

**An OG image.** `<meta property="og:image" content="https://yourdomain.com/og-image.png">`. A 1200x630 pixel image. When someone shares your link in Slack, LinkedIn, or on X, this is what shows up. Without it, the preview is an empty box. Making one in Figma takes twenty minutes.

**A canonical URL.** `<link rel="canonical" href="https://yourdomain.com/this-page">`. This tells Google which version of the page is the "real" one when there are duplicates (with and without trailing slashes, with query strings, etc.). One line. Prevents confusion.

**JSON-LD structured data.** This is a small block of JSON that describes your page in a format Google explicitly understands. More on this below. It's the one most people skip.

Most React setups handle these through a head-management library like [Unhead](https://unhead.unjs.io/) or React Helmet. The basic pattern is the same: set these five things per-route, not once globally in `index.html`.

## Sitemap and robots.txt: the boring two-file fix

A sitemap is an XML file at `yourdomain.com/sitemap.xml` that lists all the URLs on your site and when they were last updated. A robots.txt is a plain text file at `yourdomain.com/robots.txt` that tells crawlers which URLs they're allowed to visit.

Neither file is exciting. Both are necessary.

Google can find pages without a sitemap by following links, but for a new site with no inbound links, the sitemap is often the only way Googlebot finds your pages at all. Submitting it in [Google Search Console](https://search.google.com/search-console) tells Google to check it actively.

A minimal sitemap for a vibe-coded app looks like this:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://yourdomain.com/</loc>
    <lastmod>2026-07-01</lastmod>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://yourdomain.com/features</loc>
    <lastmod>2026-07-01</lastmod>
    <priority>0.8</priority>
  </url>
</urlset>
```

Include your landing page, any public marketing pages, and your blog if you have one. Don't include authenticated routes (dashboard, settings, user-specific pages). Google can't log in.

For robots.txt, the default is simple:

```
User-agent: *
Allow: /

Sitemap: https://yourdomain.com/sitemap.xml
```

That's it. That's the whole file.

## Prerendering vs SSR vs just-deal-with-it: what indie apps actually need

Here's where people get stuck. The real solution to the blank-page problem is making sure Google gets HTML with content in it before JavaScript runs. There are three ways to do that, and they are not equally worth your time as a solo founder.

**Server-side rendering (SSR)** means your server generates the full HTML for every request. It's the most complete solution. It's also the most complex to implement after the fact, especially if your tool of choice (Lovable, Bolt.new, v0) generated a pure React SPA with no server layer. Rebuilding the architecture to support SSR is rarely worth it for a solo project unless search traffic is central to the business model from day one.

**Prerendering** is the middle path. You run a build step that visits each of your public routes, waits for the JavaScript to execute, and saves the resulting HTML to disk. That static HTML is what gets served to crawlers. Tools like [Vite SSG](https://github.com/antfu-usagi/vite-ssg) or even a simple Puppeteer script can do this. It adds a build-time step but doesn't require changing your runtime architecture.

**Just-deal-with-it** is the approach many indie apps actually take: get the five meta tags right, submit the sitemap, and let Google run the JavaScript on its own schedule. Google's crawler has gotten better at running JavaScript, and for many apps the results are acceptable. You'll miss some indexing, especially for content-heavy pages, but your core landing page will probably index fine.

For most vibe-coded apps, prerendering your public marketing pages is the right call. Your logged-in app doesn't need to be indexed anyway. The landing page, pricing page, and any blog you run are the pages that drive search traffic. Prerender those five to ten routes and leave the SPA architecture for everything else.

## Schema markup that earns rich results

JSON-LD (JSON for Linked Data) is a structured data format you drop into a `<script>` tag in the page head. Google uses it to understand what kind of page it's looking at and sometimes displays extra information in search results. Those extra pieces of information (star ratings, FAQ dropdowns, breadcrumbs) are called "rich results."

Three schema types are worth adding to a vibe-coded app:

**SoftwareApplication** goes on your landing page or product page. It tells Google your page describes a piece of software:

```json
{
  "@context": "https://schema.org",
  "@type": "SoftwareApplication",
  "name": "Your App Name",
  "applicationCategory": "WebApplication",
  "offers": {
    "@type": "Offer",
    "price": "0",
    "priceCurrency": "USD"
  }
}
```

**BlogPosting** goes on each blog post. It signals to Google that this is an article, with a clear author and publication date:

```json
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Your Post Title",
  "datePublished": "2026-07-08",
  "author": {
    "@type": "Person",
    "name": "Your Name"
  }
}
```

**FAQPage** goes on a page with genuine frequently-asked questions. If you have a real FAQ section, this can generate an expandable FAQ dropdown in search results:

```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is this app for?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A direct answer in one or two sentences."
      }
    }
  ]
}
```

You can validate any of these with [Google's Rich Results Test](https://search.google.com/test/rich-results). It'll tell you whether the schema is valid and whether the page is eligible for a rich result.

## The 90-day reality check: what to expect after you fix all this

If you fix everything above this week, here is what you should realistically expect in the next three months.

The first two to four weeks: nothing visible. Google takes time to recrawl pages. Submit the sitemap in Search Console and request indexing for your main URLs. Then wait.

Weeks four through eight: your branded queries start working properly. Searching your app name returns the right page with the right description. Your public pages appear in Search Console's coverage report as "indexed." You may start to see a handful of impressions for non-branded terms.

Months two through three: if you have any content (a blog, a landing page that covers the problem clearly), you start to see traffic for long-tail queries. Not much. But some. The pages that rank are the ones with real content, not just a hero image and a CTA button.

The honest expectation for a new indie app is that organic search takes three to six months to become a meaningful traffic source, assuming you have content worth ranking. The technical fixes in this post get you indexed. They don't make thin pages rank for competitive terms. You still need to write for humans about problems humans search for.

The combination of [solid launch ops and a search-ready site from day one](/blog/vibe-coded-app-launch-checklist) is what separates the apps that slowly build organic traffic from the ones that depend on social spikes forever.

## Getting the spec right upfront saves you all of this later

Every SEO problem in a vibe-coded app is a "you didn't describe it to the AI" problem. The AI built what you specified and stopped there. Meta tags, sitemap, prerendering, structured data: none of that ends up in a vibe-coded app unless someone explicitly asked for it.

The fix after launch is annoying but manageable. The better version is including these requirements in the spec before you start building, so the AI scaffolds them from the start. A public marketing page in the spec should include: title tag, meta description, OG image, canonical URL, and JSON-LD type. Written into the spec once, it shows up in every page the AI generates.

That's the case for planning the SEO layer before you open Cursor or Lovable. Draftlytic's project planning flow asks about your public pages, your target audience, and the content you're shipping, which means the spec it generates includes enough context to prompt your AI tool to build these things the first time, not as a cleanup task six weeks after launch.
