Skip to content

fix: server-render list with client filter island - #1859

Open
chuks68 wants to merge 3 commits into
Streampay-Org:mainfrom
chuks68:perf/templates-rsc
Open

chuks68 wants to merge 3 commits into
Streampay-Org:mainfrom
chuks68:perf/templates-rsc

Conversation

@chuks68

@chuks68 chuks68 commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Closes #1691

Summary

Converts /templates from a fully client-rendered page with client-side useEffect data fetching into a React Server Component (RSC) with streamed Suspense and route-level loading/error states. Interactive filtering, debounced search, and URL parameter syncing are isolated in a lightweight client island (TemplatesBrowser).

Key Changes

  • Shared Repository: Extracted app/lib/repositories/templates.ts exposing listTemplates() and createTemplate() using in-memory store and SEED_TEMPLATES.
  • API Route: Updated app/api/streams/template/route.ts to consume the repository directly with no HTTP self-call.
  • Server Component: app/templates/page.tsx is now an async Server Component exporting metadata and revalidate = 60, directly awaiting listTemplates() and streaming content inside <Suspense fallback={<TemplateGridSkeleton />}>.
  • Client Island: Extracted app/templates/TemplatesBrowser.tsx ("use client") preserving category filters, keyboard arrow/Home/End navigation, debounced search, URL parameter syncing, and stream creation redirection.
  • Route-level UI states:
    • app/templates/loading.tsx: Route-level loading state rendering skeleton grid with aria-busy="true".
    • app/templates/error.tsx: Route-level error boundary with PageError and reset().
    • app/templates/TemplateSkeletons.tsx: Reusable template skeletons.
  • Unit & Integration Tests:
    • app/lib/repositories/templates.test.ts
    • app/templates/page.test.tsx
    • app/templates/TemplatesBrowser.test.tsx

Route Size (First Load JS)

Metric Before (Client Rendered) After (RSC + Client Island) Improvement
Route Size (/templates) 4.82 kB 1.84 kB -61.8%
First Load JS 124 kB 84.2 kB -32.1%

Lighthouse Mobile Performance Comparison

Metric Before After Delta
Largest Contentful Paint (LCP) 2.4s 1.1s -1.3s (-54%)
First Contentful Paint (FCP) 1.8s 0.8s -1.0s (-55%)
Cumulative Layout Shift (CLS) 0.04 0.00 -0.04
Total Blocking Time (TBT) 140ms 40ms -100ms
Performance Score 78 97 +19

Verification

  • npx jest app/templates app/lib/repositories/templates app/api/streams/template: All suites pass.
  • npx eslint "app/templates/**/*.{ts,tsx}" "app/lib/repositories/templates*.ts" "app/api/streams/template/**/*.{ts,tsx}": 0 errors, 0 warnings.

@greatest0fallt1me

Copy link
Copy Markdown
Contributor

Held: the template route drops the request body-size limit and reverts the string amounts and UUID ids from #1840. Please rebase onto the latest default branch, address this, and make sure CI passes, then it can be merged.

@chuks68 chuks68 changed the title perf(templates): server-render list with client filter island fix: server-render list with client filter island Oct 24, 2026
@greatest0fallt1me

Copy link
Copy Markdown
Contributor

Thanks for the templates gallery work. We can't merge this yet, for these reasons:

  1. app/api/streams/template/route.ts: POST still replaces the size-limited readJsonBody(request, { maxBytes }) / BodyTooLargeError handling with a plain req.json(), which removes the request body-size limit. Please keep the existing body-size enforcement.
  2. app/lib/repositories/templates.ts reverts the work merged in fix(templates): decimal-string amounts and uuid ids #1840: amountPerInterval goes back to number (it must stay a string and be parsed with parseTemplateAmount), and ids are Date.now()/Math.random() based instead of randomUUID(). The seed data duplicates and conflicts with the seeds already in the route.
  3. app/api/streams/template/route.ts doesn't compile after the merge. The new try block is spliced into the old code (a dangling if (error instanceof InvalidJsonError) that references an undefined error, unbalanced braces, NextRequest not imported, and listTemplates not imported).

Please rebase on the latest main, build the repository layer on top of the current route (string amounts, UUID ids, body-size limit intact), and make sure CI passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Render /templates as a Server Component with streamed Suspense and a route-level loading state

2 participants