Server vs. Client Components
Understand the dual-component architecture of React 19 and how Dinou blends server-side safety with client-side interactivity.
Overview
Dinou is built around the React 19 Server Components (RSC) model. Instead of treating your application as a purely client-side React app, Dinou splits components into two categories:
Server Components
In Dinou, every component is a Server Component by default. They are executed on the server to output a JSON-like representation of the UI (the React Flight payload) which React uses to construct the DOM.
What they can do:
- Direct Database Access: Fetch data directly using queries or server-side SDKs.
- Secure Execution: Keep secrets, API tokens, and private logic hidden from client exposure.
- No Bundle Overhead: Large node packages used inside Server Components are never downloaded by the browser.
What they cannot do:
- Cannot use state or effect hooks (
useState,useEffect,useReducer). - Cannot access browser-only APIs (
window,document,localStorage). - Cannot add client event listeners (like
onClickoronChange).
// src/user-list/page.tsx (Server Component by default)
import { getContext } from "dinou";
export default async function Page() {
// Direct backend context access!
const query = getContext().req.query;
const users = await db.users.findMany({ q: query.search });
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}Client Components
Client Components represent components that have access to client-side interactivity. They are still pre-rendered on the server to HTML for fast initial page load times, but their javascript bundle is sent to the client to hydrate the interactive elements.
What they can do:
- Use state and effects (
useState,useActionState,useEffect). - Interact with the browser (event listeners, access to
window, etc.). - Consume Client-only Context providers.
Server Pre-Rendering Notice
window) directly in the body of a Client Component will cause server crashes unless deferred inside a useEffect block.The "use client" Directive
To designate a component as a Client Component, add the "use client" directive at the very top of the file, before any import statements.
"use client"; // Marks this file and all its imports as Client-side
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}Once a file contains "use client", a boundary is established. Any file imported by this component is automatically treated as a Client Component as well, without needing the directive.
Data Serialization Boundary
When passing data across the network boundary from a Server Component to a Client Component, the props must be serializable.
- Primitives (strings, numbers, booleans)
- Plain Objects & Arrays
- Promises (passed from server, read via React 19
use()hook)
- Functions (cannot pass callback event handlers directly)
- Class instances (prototypes and methods are lost; React throws serialization errors)
- Browser-only globals and DOM elements (e.g.,
window,document, HTML nodes)
Passing Promises (React 19 Feature)
use() hook, suspending the component tree automatically until the promise resolves.Choosing Your Architecture
Dinou does not force a single "correct" pattern. Depending on your goals and preferences, you can choose the paradigm that best fits your development flow:
Pattern A: The Client-First Model (getProps + Client Components)
If you prefer the mental simplicity and freedom of standard React, you can build your pages entirely as Client Components and fetch server-side data using the getProps function of page_functions.
- Freedom & Interactivity: You write familiar React code with full access to hooks and browser event listeners (like
onClick) anywhere in the file. - No Mental Splitting: No need to separate server and client boundaries inside the page file.
- Secure Data Fetching: The data in
getPropsstill executes securely on the server (accessing databases/keys) and passes the results as props. - Trade-off: The JavaScript bundle shipped to the browser includes the component logic and its imports.
Pattern B: The Server-First Model (Server Components)
If you are building content-heavy pages (like blogs, documentation, or landing pages) and want to minimize the JavaScript payload sent to the client, you can write them as Server Components.
- Ultra-Lightweight: The code for Server Components is never sent to the browser. Only the rendered HTML elements are shipped.
- Zero Bundle Overhead: Heavy libraries imported inside Server Components do not inflate the client bundle.
- Trade-off: You must explicitly split interactive elements (like custom buttons or input fields) into separate files marked with
"use client".