Challenge: Keep the Shell Stable with Local RSC Simulation

Learn how to simulate the RSC mental model locally using use() and Suspense to keep a stable shell while slower regions stream in.

Problem statement

You’re building a Support Ticket Desk UI for a larger product. The users should see a stable shell and useful structure immediately, while slower regions progressively appear as data and code become available. Use Suspense boundaries to keep retries localized, design layout-stable skeletons for fallbacks, and prefer intent-based preloading (hover/focus) for optional panels so the UI feels progressive rather than blocked.

Success criteria

  • The shell never disappears (header + columns remain mounted during loading/retry).

  • Once the ticket queue is visible, selecting a ticket must not re-suspend the queue.

  • Selecting a ticket shows a fast header (subject + status) that remains visible while the slow conversation thread streams in.

  • If the thread fails, show a recovery UI with Retry that refetches only the thread region.

Technical requirements: Implement the following features step by step:

  • Task 1: Cache layer (RSC-like locally)

    • Implement read(key), preload(key), invalidate(key) in cache.js so use() can suspend.

  • Task 2: API wrappers (server seam)

    • Implement wrapper functions in api.js that call fakeServer() with keys:

      • getTicketQueue()"queue"

      • getTicketHeader(id)header:${id}

      • getTicketThread(id)thread:${id}

      • preloadTicket(id) → preload header + thread

  • Task 3: TicketQueue region (Suspense + intent preload)

    • Build the queue UI using use(getTicketQueue()).

    • Add hover/focus intent preloading: preloadTicket(id).

  • Task 4: TicketViewer region (nested Suspense + localized retry)

    • Fast header under Suspense (use(getTicketHeader(id)))

    • Slow thread under nested Suspense + ErrorBoundary

    • Retry invalidates only thread:${id}

Project structure