There is a pattern where you can trigger a Server Function call from within a Client Component, that returns another Client Component, wrapped in Suspense.
This pattern works seamlessly in Dinou, a React 19 framework, and with some current limitation in Waku, another React 19 framework.
I wrote a post about it.
But I will put here the code anyway (using Dinou). It's very simple.
First we define the Server Function.
"use server";
// import the Client Component
import UserProfile from "@/components/user-profile";
export async function userProfile() {
// Fetch from DB or API
const name = await new Promise((resolve) => setTimeout(() => resolve("John"), 3000));
// Return Client Component with props filled with data
return <UserProfile name={name} />;
}
Next we define the Client Component returned by the Server Function.
"use client";
export default function UserProfile({name}){
return <div>{name}</div>
}
Finally, we define the page component, a Client Component, from where we trigger the call to the Server Function.
"use client";
import Suspense from "react-enhanced-suspense";
import { userProfile} from "@/server-functions/user-profile";
export default function Page() {
return (
<div>
<Suspense fallback="Loading..." resourceId="user-profile">
{() => userProfile()}
</Suspense>
</div>
);
}
As you can see I use Suspense from react-enhanced-suspense in the example shown. This is because when used like this, the promise returned by the Server Function will remain stable between re-renders of the Client Component, and only be re-invoked, the Server Function, when resourceId changes.
Then we can do something like this:
"use client";
import Suspense from "react-enhanced-suspense";
import { aServerFunctionWithArgs} from "@/server-functions/a-server-function-with-args";
import { useState } from "react";
export default function Page() {
const [arg1, setArg1] = useState("foo");
return (
<div>
<Suspense fallback="Loading..." resourceId={`user-profile-${arg1}`}>
{() => aServerFunctionWithArgs(arg1)}
</Suspense>
</div>
);
}
In this last case the Server Function will be re-invoked dynamically whenever its arguments change.
Suspense from react-enhanced-suspense it's exactly React's Suspense when no extra prop is used.