Experiment in implementing React Server Components without using a framework, a bundler or any other dependencies
Without React:
open html/index.html
With Client Side React:
(cd hydrate && npm start)
With Server Side React:
(cd react && npm start)
- Without React
- Pass server props to client components
- Server actions
- With client side React
- hydrateRoot
- include reactJSON in HTML
- generate reactJSON from App.js
- components
- render HTML from clientJSON
- renderStyle
- skip server side rendering of function props, like onChange
- hydrate client components manually
- hydrate client components from JSON
- hydrateRoot(document)
- navigation (which is where hydrateRoot is leveraged)
- server action
- With plain React
- Async server components
- Fetch "react" and "react-dom" UMD in client
- importmap to enable isomorphic
import from "react"
(branch: "isomorphic-react-wip") - Hydration
- Client components
- No dependencies but React
- Example app
- One tree
- Initial fetch contains pre-rendered HTML
- Server components rendered on server
- Server components not imported on client
- Importing client components switches to client rendering
- Client components re-renderes on client
- Nesting client components in server components (upvote button)
- Nesting server components in client components (top-level dark/light mode toggle)
- Server components can access server resources (like database)
- Server components can use heavy libs (like syntax highlighting)
- Client components can access client resources (like click events)
- Testability
- Developer Experience
- Bundler
- TypeScript
- Tests
- How are server side props passed to client for hydration?
- They are included in the serialized reactTree
- Must the entire reactTree be sent to the browser, essentially duplicating the HTML?
- Yes!
- How to include the markup but not the event handler in a client
<UpvoteButton />
?- We want to include the button to avoid layout shift
- We want to postpone the button to avoid click before active
- Can we use a headless pattern?
- Parent: client component that has all event handlers and state
- Child: server component with markup
- The server simply also renders client components
- How is a server action invocation serialized to a request and a response?
- Generate an async client function that calls fetch()
- Wrap the server action with an API end-point handler
- How to render an async server component?
- Use renderToPipeableStream
- Use react@18.3
- Can client components import server actions
- Yes
- So only server components ("use server" functions that return React.Element) is prevented from being called from "use client" functions
- Why must the client bootstrap script import all server components to hydrate?
- Seems to load too much JS code to the client...?
- Why is there no react-dom/client UMD build?
- Loading react-dom gives "React.Scheduler is undefined"
- Also load the "scheduler" package!
- Why is top-level "this" undefined when importing a UMD bundle from a module?
- RSC bundler creates bundle with client components and server action client functions
- RSC server run top server component (App) to build reactTree
- RSC server render client components gets placeholders?
- RSC server render reactTree to HTML
- RSC server serialize reactTree to reactJSON
- RSC server embeds reactJSON in HTML
- RSC server embeds
<script>
link to RSC client in HTML - RSC server embeds
<script>
link to client component bundle in HTML - RSC server respond with HTML
- Browser renders HTML
- Browser loads RSC client
- RSC client derializes reactJSON into reactTree
- RSC client hydrates DOM with reactTree
- Browser imports client components from bundle
- RSC client instantiates the placeholder client components
- Browser imports server action client functions from bundle
- server action client function sends request to the server
- RSC server routes request to server action
Videos:
Blogs: