Why I Built This
I wanted a project that would push me into the bleeding edge of Next.js 15 — Partial Prerendering, Server Actions, the after() API. Reading docs and RFCs isn't enough; I needed a real app with real data flows to understand how these features behave in production.
A startup pitch platform was the right shape for the problem: authenticated users creating content, a public feed with filtering and search, and a CMS backend for structured data. Complex enough to exercise every new feature, simple enough to ship as a single developer.

How It Works
Founders sign in with GitHub OAuth and submit their startup pitch — a title, description, category, and optional media. Pitches are stored in Sanity CMS, which gives me a headless content layer with real-time previews and GROQ-powered queries.
The public feed renders with Partial Prerendering: the shell is static HTML served from the edge, while dynamic content (user-specific data, view counts) streams in via Server Components. This gives visitors a near-instant first paint without sacrificing personalization.
Authentication is handled by Auth.js with GitHub OAuth. Session tokens are managed server-side, and protected routes use middleware-level checks so unauthorized requests never reach the page component.
Key Decisions
Sanity CMS over a custom database
For a content-heavy app with structured data, Sanity was a better fit than rolling my own Prisma + PostgreSQL layer. GROQ queries are expressive, the Studio gives me a visual editor for content, and real-time listeners mean the feed updates the moment a pitch is published.
Partial Prerendering for the feed
The pitch feed is the highest-traffic page. With PPR, the layout and navigation render as static HTML at the edge, while the actual pitch list streams in as a Server Component. Users see content faster than a traditional SSR approach, and I avoid the staleness problem of full static generation.
Server Actions for mutations
Creating and editing pitches uses Server Actions instead of API routes. This keeps the data flow colocated with the UI component, eliminates client-side fetch boilerplate, and gives me progressive enhancement for free — forms work even with JavaScript disabled.
What I Learned
Bleeding-edge features require patience. Next.js 15's experimental APIs had sparse documentation and occasional breaking changes. I spent time reading source code and GitHub discussions to understand edge cases that no tutorial covered yet.
GROQ is powerful but has a learning curve. Coming from SQL, GROQ's pipeline-style syntax felt unfamiliar at first. Once I understood projections and joins, I could write queries that would have been multi-step operations in a relational database.
Design systems compound. Using Shadcn UI + Tailwind + Motion.dev together forced me to think about component composition and animation hierarchy. The upfront investment in a consistent design language paid off every time I added a new page.