Architecture
A quick tour of how the starter is organized and why each piece is where it is.
Folder layout
saasforge-starter/
├── content/
│ ├── blogs/ # MDX blog posts
│ └── docs/ # MDX docs (you're here)
├── public/
│ ├── integrations/ # Integration logos
│ └── logo/ # Brand logos
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── (legal)/ # /terms, /privacy, /license grouped
│ │ ├── blog/ # Blog listing + [slug]
│ │ ├── docs/ # Docs listing + [slug]
│ │ ├── layout.tsx # Root layout + fonts
│ │ ├── page.tsx # Home
│ │ ├── opengraph-image.tsx
│ │ ├── robots.ts
│ │ ├── rss.xml/
│ │ └── sitemap.ts
│ ├── components/
│ │ ├── blog/ # Blog listing + post card
│ │ ├── docs/ # Docs sidebar + nav
│ │ ├── ui/ # shadcn/ui primitives
│ │ ├── seo/ # JSON-LD schemas
│ │ └── [...sections] # Hero, Features, FAQ, CTA, etc.
│ ├── config/
│ │ ├── app.ts # Metadata
│ │ ├── blog.ts # Blog post index
│ │ ├── brand.ts # Brand strings
│ │ ├── docs.ts # Doc index
│ │ ├── marketing-copy.ts
│ │ ├── routes.ts
│ │ ├── seo.ts
│ │ └── ui/ # Per-section configs
│ └── lib/
│ └── utils.ts # cn() helper, etc.
└── next.config.ts
Rendering model
The starter is React Server Components by default. Only components that need hooks (useState, useTheme) or browser APIs get "use client".
app/page.tsx→ server component, reads config staticallyapp/blog/[slug]/page.tsx→ server component, reads MDX at request time (or build time)components/theme/theme-provider.tsx→ client (needsnext-themes)components/ui/button.tsx→ server-compatible (no hooks)
Route groups
src/app/(legal)/ is a route group: the parenthesized name doesn't appear in the URL. It lets us share a layout for /terms, /privacy, and /license without affecting the URL structure.
Static + ISR
Most pages pre-render at build time (○ Static in the build output). Blog posts and doc pages use ISR with 1-hour revalidation (● SSG + revalidate: 3600). That means:
- First request after build: served from the static HTML
- After 1 hour: Next.js regenerates in the background
- Next request: gets the fresh HTML
- You can trigger on-demand revalidation via
revalidatePath()when you need instant updates
Typed config
Every config file under src/config/ui/ exports a typed object. The types live in src/config/ui/types.ts. TypeScript errors catch:
- Deleted required fields
- Wrong field types (e.g.,
icon: "github"→ icon must be a known key) - Typos in referenced keys
This is how the starter achieves "rebrand without grepping strings."
Theme token flow
globals.css :root {
--primary: oklch(...);
--color-primary: var(--primary); // Tailwind v4 mapping
}
↓
shadcn/ui Button component
className="bg-primary text-primary-foreground"
↓
Tailwind v4 utility
background-color: var(--color-primary);
Change --primary once, every bg-primary / text-primary / border-primary updates. The .dark { --primary: ... } block overrides for dark mode.
MDX pipeline
- Page route calls
readFile(content/path/file.mdx) next-mdx-remote/rscparses MDX on the serverremark-gfmplugin adds GitHub-flavored markdown support- Our custom
mdxComponentsmap overrides<a>,<img>,<pre>,<code>, tables, etc. to usenext/link+next/image+ theme-aware styling - Rendered HTML streams to the browser as React Server Component output
Why no auth / DB / billing?
Because every product makes different choices. Forcing a stack (Supabase + Stripe + Resend) would mean:
- You pay for complexity you don't use
- Swapping vendors is expensive
- The starter has opinions beyond its scope
When you need those layers, pick the vendor yourself: the starter stays out of your way.
If you're building a product where auth + workspaces + billing are the main features (not a content site), use SaaSForge Core: it's the same design system with those layers wired in.
Next
- Getting Started: setup guide
- Customization: config reference