> ## Content Index
> Fetch the complete content index at: https://aresa.me/llms.txt
> Use this file to discover other available public pages before exploring further.

# Making a React Marketing Site Crawlable Without Adopting a Framework
- URL: https://aresa.me/crawlable-react-marketing-site-without-framework/
- Published: 2026-08-26T20:39:46.000Z
- Updated: 2026-08-26T20:39:46.000Z
- Description: Signalbin kept its Vite React application and prerendered a fixed marketing route manifest into complete HTML, with tests to stop metadata drift.
- Author: Alex Oleshkevich
- Tags: Dev Log, Signalbin

Signalbin's marketing routes looked complete after JavaScript ran, but their built HTML was the same empty app shell with homepage metadata. I kept the Vite React application and added a small prerendering pipeline for the finite set of public pages.

This was a good fit because the marketing site had a fixed route manifest and no per-request data. It would be a poor substitute for a framework if pages depended on request-time personalization or a large dynamic content graph.

## One manifest owns the public routes

The first problem was duplication. The router knew which URLs existed, the navigation knew which links to show, the SEO component knew their titles and descriptions, and a prerender script would need the same list again.

Signalbin now keeps marketing routes in one manifest. The application router and build script consume it, and an assertion script checks that each route produces the expected title, description, canonical URL, and body marker.

That invariant matters more than the rendering API. A prerenderer that silently skips a new route recreates the original problem while leaving the build green.

## Vite loads the server renderer during the build

After the client bundle is built, a Node script starts Vite in middleware mode and loads the server-side render functions directly from source. For every public route, it renders the page, removes head elements managed by the app shell, inserts route-specific metadata, and writes a complete `index.html` under the matching output path.

React provides server rendering through [0](https://react.dev/reference/react-dom/server?ref=aresa.me). Signalbin uses that capability as a build step rather than adopting a framework runtime. The browser still hydrates the normal application after loading the generated HTML.

The catch-all app shell remains for authenticated and client-only routes, but it carries `noindex`. Crawlers get complete public pages, while an unknown client route cannot inherit the homepage title and present itself as a valid marketing page.

## Build assertions make static output trustworthy

The verification script reads the generated files rather than trusting that rendering ran. It fails when a route is missing, when a title or canonical URL belongs to another page, or when the expected rendered content is absent.

This catches the common failure mode where local navigation works because React renders after load, but direct HTML inspection still shows an empty root. It also catches metadata drift between the route manifest and the built document.

A framework would provide routing, data loading, head management, and prerendering as one integrated system. For Signalbin's small static marketing surface, migrating the whole application would have been more machinery than the problem required. A route manifest, a server render function, and build-time assertions created the artifact the crawler actually reads.