A minimal SSR React framework built on Bun. Implements the pre-RSC Next.js model: file-based routing, server-side rendering with getServerSideProps, per-page client bundles, hydration, and client-side navigation with loading skeletons.
This is a teaching codebase. It is not a production framework. The goal is to show how the pieces fit together - the router, the build pipeline, the server renderer, hydration, and the dev mode loop.
SmoothJS is a minimal implementation of how React frameworks worked before React Server Components (RSCs). It exists to teach the core concepts: SSR, hydration, file-based routing, per-page bundling, and the server-client data handoff.
For a deep dive into every module, how the build pipeline works, how the router matches URLs, how the server handles requests, and how the client-side router navigates between pages, read pkg/README.md.
This project covers the pre-RSC era - the Next.js model before React Server Components. The natural next step is implementing RSCs: components that run and render entirely on the server, sending only their output (not their code) to the client. This is how modern Next.js works. The concepts here (SSR, hydration, per-page bundles) are the foundation that RSCs build on.
# Install dependencies
bun install
# Run the dev server (builds + watches for changes + auto-reload)
bun run dev
# Production build
bun run build
# Start production server
bun run startOpen http://localhost:3000.
The pages/ directory contains a sample application built to test this framework. It includes pages (home, blog, about), dynamic routes (blog/[slug]), nested layouts, getServerSideProps data fetching, client-side state (a counter), loading skeletons, and Tailwind CSS. You can modify it like a normal pre-RSC Next.js app - add pages, change layouts, wire up a database, or replace it entirely.
pages/
_app.tsx # Root app wrapper (wraps every page)
page.tsx # Home page (/)
blog/
page.tsx # Blog index (/blog)
[slug]/
page.tsx # Blog post (/blog/:slug)
loading.tsx # Loading skeleton for client-side navigation
about/
page.tsx # About page (/about)
globals.css # Tailwind CSS
pkg/ # Framework internals
bin/smooth.ts # CLI entry point
- File-based routing.
pages/blog/[slug]/page.tsxbecomes/blog/:slug. Static and dynamic routes with nested layouts. - Server-side rendering. React renders to HTML on the server. The browser gets a complete page with real content. No blank white screen.
- Per-page client bundles. Each page gets its own JavaScript bundle containing only that page's components and layouts. No monolithic app.js.
- Client-side hydration. React takes over the server-rendered HTML in the browser. The page is visible immediately and becomes interactive after hydration.
- Client-side navigation. Clicking an internal link fetches only JSON data for the new page and swaps the content without a full page reload.
- Loading skeletons. Place a
loading.tsxnext to anypage.tsx. It shows while the new page's data loads during client-side navigation. - Dev mode with auto-rebuild. Save a file, the server rebuilds and the browser reloads automatically.
- Bun 1.3+
- React 19
