The defer() function enables deferred rendering for React Server Components, reducing initial data load.
You can think of this as React's lazy API but for Server Components.
// @funstack/static/server is where utilities for server components live
import { defer } from "@funstack/static/server";
import { defer } from "@funstack/static/server";
import { HeavyServerComponent } from "./HeavyServerComponent";
function Page() {
return (
<details>
<summary>Very long description</summary>
<Suspense fallback={<p>Loading...</p>}>
{defer(<HeavyServerComponent />)}
</Suspense>
</details>
);
}
By using defer(), the HeavyServerComponent will still be rendered on the server (during build), but its data will be sent to the client as a separate RSC payload.
This means that:
HeavyServerComponent's datadefer(<HeavyServerComponent />) part is rendered on the client, it will fetch the separate RSC payload and show the content.The key point is that HeavyServerComponent is still a Server Component, so only the rendered HTML (and usage of Client Components inside it) is sent to the client, not the component code itself.
Note: defer() must be used inside a Suspense boundary since the content will be streamed in later.
export function defer(element: ReactElement, options?: DeferOptions): ReactNode;
interface DeferOptions {
/**
* Optional name for debugging purposes.
* In development: included in the RSC payload file name.
* In production: logged when the payload file is emitted.
*/
name?: string;
}
/funstack__/fun__rsc-payload/HomePage-b5698be72eea3c37)A React Node that will stream its content separately from the main entry point.
Use defer() when you have components that:
Typically, you will want to wrap route components with defer() to improve initial load performance. Otherwise, user needs to wait for contents for all pages to arrive before seeing anything.
import { defer } from "@funstack/static/server";
import HomePage from "./HomePage";
import AboutPage from "./AboutPage";
const routes = [
route({
path: "/",
component: defer(<HomePage />, { name: "HomePage" }),
}),
route({
path: "/about",
component: defer(<AboutPage />, { name: "AboutPage" }),
}),
// ...
];
Using the name option makes it easier to identify which deferred component corresponds to which payload file during development and in build logs.
By default, FUNSTACK Static puts the entire app (<App />) into one RSC payload (/funstack__/index.txt). The client fetches this payload to render your SPA.
When you use defer(<Component />), FUNSTACK Static creates additional RSC payloads for the rendering result of the element. This results in an additional emit of RSC payload files like /funstack__/fun__rsc-payload/b5698be72eea3c37. If you provide a name option, the file name will include it (e.g., /funstack__/fun__rsc-payload/HomePage-b5698be72eea3c37).
In the main RSC payload, the defer call is replaced with a client component <DeferredComponent moduleId="fun__rsc-payload/b5698be72eea3c37" />. This component is responsible for fetching the additional RSC payload from client and renders it when it's ready.