Splitting Next.js Server and Client Components for a Redis-Backed Sidebar
The RSC/client boundary is the central tension in Next.js App Router architecture. This walkthrough makes the split concrete: server components fetch and shape data, while a thin client wrapper reserves space for `useState` and event handlers without pulling the entire list into the browser bundle.
A Redis hash stores serialized note data, and a four-layer Next.js component tree separates server-side data fetching from client-side interactivity using `children` slots and a `"use client"` boundary. The data layer uses `ioredis` with a seed-data pattern: `hgetall` checks for an empty hash, and `hset` populates sample notes on first run so the UI is never blank. The sidebar component tree splits responsibilities across `Sidebar` (async data fetch), `SidebarNoteList` (array conversion and iteration), `SidebarNoteItem` (date formatting and prop preparation), and `SidebarNoteItemContent` (the `"use client"` boundary reserved for future interaction like expand/collapse). A pre-split version, `SidebarNoteList2`, is kept for comparison to show how inlining everything into one component blocks reuse and interactivity.
The seed-data pattern—checking for an empty hash and populating it on first access—turns a database into a self-initializing module, removing setup steps for demos and development.
Keeping a pre-split component file alongside the refactored version is a practical teaching and comparison technique that production codebases rarely preserve but tutorials benefit from.
The `"use client"` boundary is placed on the innermost leaf component (`SidebarNoteItemContent`), not on the list or item, which keeps the maximum amount of rendering on the server and minimizes client-side JavaScript.
Passing JSX fragments as props (`expandChildren`) rather than strings or flags is a slot pattern that lets the server component own the markup while the client component controls visibility—a clean separation of concerns.