React 19 Server and Client

The main features introduced in React 19 is the distinction between server and client components.

Server components

Server components are entirely executed and rendered on the server. They can execute any asynchronous logic to fetch required data to then generate the HTML for the initial page load.

Therefore, server components have access to features that are not available in the browser:

  • Reading file system
  • Accessing environment variables (even secret variables)
  • Querying databases

Furthermore, all the dependencies used inside server components are not bundled with the client code. This allows installing dependencies without costs in performance or bundle size for the client.

However, server components are not interactive. A server component is rendered only once and then sent to the client. As such, server components don't have access to browser APIs like window or document.

Client components

When a component needs to be interactive on the client it must be marked as a client component.

An interactive component is anything that needs access to hooks (like useState or useEffect) or browser APIs like window or document.

When a client component is included inside a server component, React will render the server-only HTML on the server and then hydrate the client-only HTML on the client.

For more details on how server and client components work, check out the React 19 documentation.

Server and client code

Since there is a clear distinction between server and client, in React 19 we need to organize the code accordingly.

Code running on the server has access to different APIs than code running on the client. It helps to make this distinction explicit to avoid bugs and unexpected behaviors.

Effect is key here, since it allows to organize the code on top of services and layers.

If you have no idea what services and layers are, check out their modules in my effect course.

We will build the logic of the app on generic services, and define the specific client and server implementations inside different layers.

We will then compose different runtimes, so that we have a clear separation of features available on the client and server.