Back to writing

Development / Decision

Client-rendered apps and SEO

Vidpipe taught me where a client-rendered React app stops being fine, and where static HTML needs to take over.

Brad TraversyJune 29, 2026React
Generated article editor inside the Vidpipe product
VidpipeWhere the product app and the public web started needing different things.

Vidpipe started as an app, not a publication.

Paste a YouTube URL, generate an article, edit it, connect a channel, review new outputs, manage billing. Those are app screens. They have state, authentication, forms, modals, and background jobs. A client-rendered React app is a perfectly reasonable shape for that.

Then the public site grew around it. Homepage, pricing, terms, privacy, and feature pages. Public product pages are not app screens. They are documents with a job: load fast, explain the product, show crawlers the real content, and share cleanly in previews.

That is where the original shape broke down.

The failure mode

The visible site looked fine in a browser. Open the homepage, React booted, the router mounted, and the content appeared. There was no obvious bug.

View the raw HTML and the problem was right there: a root element, a script bundle, and not much else.

For an authenticated dashboard, that is fine. The user is already there. Google is not the audience. A blank app shell that hydrates into the real UI is a normal tradeoff.

For public marketing pages, it is a problem. Crawlers are better at rendering JavaScript than they used to be, but “Google can probably render this later” is not a strategy. Neither are social cards, link unfurlers, small search bots, or the random tool that grabs your page once and never waits for the app.

The page needs to be a page before React starts.

The three real options

  1. Migrate the app to an SSR framework

    The clean architectural answer from scratch, but too much migration and permanent server-rendering overhead for an established client app.

  2. Split marketing into a static site

    Clean in theory, but it creates a second app, duplicated design work, and another deployment surface to maintain.

  3. Prerender the public routes

    Keep the existing app shape, render only the public pages during the build, and save those pages as static HTML.

The third path was the right one for Vidpipe.

Prerendering is not magic

This is not a new rendering model. It is a build step. A script starts a preview server against the built frontend, opens each public route, waits for the app to settle, and writes the resulting HTML to the static output folder.

The important part is the route list. Marketing pages are in. App routes are out.

/          serves real HTML
/pricing   serves real HTML
/privacy   serves real HTML
/dashboard falls through to the app

No database call at request time. No server runtime for the marketing pages. No framework migration.

Vidpipe dashboard with content generation and channel management tools
The authenticated product remains a client-rendered application because interaction and user state matter more than search visibility.

Canonicals matter more than the script

Prerendering the body was only half the fix. The first pass exposed a different problem: route metadata had been treated like an app concern instead of a document concern.

Some public routes inherited the homepage canonical. Some had thin descriptions. The sitemap only covered a small slice of the site. That is the kind of thing you miss when every page is just “the app.”

01  Unique title
02  Meta description
03  Route-specific canonical URL
04  Open Graph title and description
05  Sitemap entry

That list is boring. It is also the difference between technically rendering a page and publishing a real document on the web.

Client-rendered is still fine

The conclusion is not that client-rendered apps are bad for SEO. Different routes simply have different jobs.

Logged-in dashboard

  • Authentication state matters
  • Interaction matters
  • Loading states are acceptable
  • Client routing is a good fit

Public product page

  • The first HTML response matters
  • Metadata matters
  • Social previews matter
  • JavaScript should improve the page

Those routes can live in the same codebase. They should not pretend to be the same kind of page.

The rule I kept

If a route is trying to acquire a user, it should be a document first and an app second.

If a route is helping an existing user do work, it can be an app first.

That split is the whole lesson. The mistake was not choosing a client-rendered app. The mistake was letting the marketing pages inherit that choice after their job changed.

Vidpipe did not need a new framework. It needed its public pages to exist before React arrived.