I built CFB27 Nation (https://cfb27.com), a companion site for EA Sports College Football 27 players. Here's the tech breakdown.
THE STACK:
- React Router v7 framework mode (ssr:false + prerender)
- 144 pre-rendered static HTML pages at build time
- PostgreSQL 17 + PostgREST for the data layer
- Caddy for serving — pure file_server, no Node runtime in production
- Three deployment slots (prod/test-a/test-b) on a single $6/month VPS
WHY STATIC OVER SSR:
I tried Next.js SSR first. It added unnecessary runtime complexity for a content-heavy site. With React Router v7 SSG, every page is a dead HTML file. Zero server-side rendering at runtime. Caddy serves them like it's 1999. The "hydration" still kicks in to make interactive tools work, but the first paint is instant.
WHAT'S PRE-RENDERED:
- 21 static route pages (home, about, contact, etc.)
- 113 article pages (game guides, ratings, news)
- 8 legal pages (privacy, terms)
- 2 tool sub-routes
= 144 HTML files generated at npm run build
THE DATA LAYER:
PostgREST exposes PostgreSQL as a REST API. The build process queries it to generate all article pages. A thin BFF (Express) handles write operations and analytics. No ORM, no GraphQL — just SQL views and HTTP.
WHAT I LEARNED:
- RRv7's prerender() is surprisingly good for content sites. 144 pages build in under 30 seconds.
- PostgREST + PostgreSQL views eliminate most backend code.
- Caddy handles SSL, routing, and file serving in one binary. No nginx config hell.
- Three deployment slots on one VPS is possible with symlinks and port-based routing.
The site is live at https://cfb27.com. All the tools work with zero logins. Happy to answer questions about the stack!
react #webdev #ssg #postgresql #buildinginpublic