-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Overview
Build the React Server Components (RSC) + streaming version.
Dependency: Task 2 (Shared Components & API)
Can Run Parallel With: Task 3 (Traditional Version)
Key Architecture
- Root component is async function WITHOUT
"use client" - Components go into the RSC bundle
- Entire page is SSRed - both server components AND any client components
- Data fetched server-side via
getReactOnRailsAsyncProp - HTML streamed to browser as Suspense boundaries resolve
Deliverables
Rails View
<!-- app/views/restaurants/search_rsc.html.erb -->
<div class="search-page">
<%= stream_react_component("SearchPageRSC", { restaurant_id: @restaurant.id }) %>
</div>Components
| Component | Type | Purpose |
|---|---|---|
SearchPageRSC.tsx |
Server (async) | Entry point, no hooks |
AsyncStatus.tsx |
Server (async) | Awaits status server-side |
AsyncWaitTime.tsx |
Server (async) | Awaits wait time server-side |
AsyncSpecials.tsx |
Server (async) | Awaits specials server-side |
AsyncTrending.tsx |
Server (async) | Awaits trending server-side |
AsyncRating.tsx |
Server (async) | Awaits rating server-side |
Pattern
// SearchPageRSC.tsx - Async server component
async function SearchPageRSC({ restaurant_id }: Props) {
return (
<div>
<RestaurantCardHeader restaurantId={restaurant_id} />
<Suspense fallback={<Spinner />}>
<AsyncStatus restaurantId={restaurant_id} />
</Suspense>
</div>
);
}
export default SearchPageRSC;
// AsyncStatus.tsx - Server component (async, no hooks)
async function AsyncStatus({ restaurantId }: Props) {
const status = await getReactOnRailsAsyncProp('status', { restaurantId });
return <StatusBadge status={status} />;
}
export default AsyncStatus;Rails Async Prop Helpers
# app/helpers/async_props.rb
module AsyncProps
def self.status(restaurant_id:)
restaurant = Restaurant.find(restaurant_id)
{ status: restaurant.current_status, timestamp: Time.current.to_i }
end
# ... similar for wait_time, specials, trending, rating
endPerformance Monitoring
- Collect Web Vitals (same as Traditional)
- Measure "Stream Complete Time", "Server Fetch Time"
Success Criteria
-
/search/rscpage loads and renders - All data fetched server-side (before rendering)
- HTML streamed to browser with Suspense boundaries
- Spinners resolved on server (no client-side spinners visible)
- API calls ONLY on server (no
fetch()in browser Network tab) - LCP: ~200-250ms
- CLS: ~0.02 (no layout shifts)
- 59% faster than Traditional (550ms → 225ms)
- Console shows no errors
Server Component Constraints
Server components CANNOT:
- Use
useState,useEffect,useCallback, etc. - Use
useContext - Use browser APIs (localStorage, window, etc.)
- Be marked with
"use client"
Server components CAN:
- Be declared as
async function - Await data fetching
- Access Rails helpers and models
- Render pure display components
- Include client components lower in the tree (with
"use client")
Key Files to Create
app/javascript/entries/search_rsc.tsx
app/javascript/components/async/rsc/
├─ SearchPageRSC.tsx
├─ AsyncStatus.tsx
├─ AsyncWaitTime.tsx
├─ AsyncSpecials.tsx
├─ AsyncTrending.tsx
└─ AsyncRating.tsx
app/views/restaurants/search_rsc.html.erb
app/helpers/async_props.rb
coderabbitai
Metadata
Metadata
Assignees
Labels
No labels