From ab19f02012af7f6562942ddf57988ee537a44072 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 28 Jul 2025 14:05:11 +0200 Subject: [PATCH 1/3] Add page on server components --- data/sidebar_react_latest.json | 3 +- pages/docs/react/latest/server-components.mdx | 190 ++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 pages/docs/react/latest/server-components.mdx diff --git a/data/sidebar_react_latest.json b/data/sidebar_react_latest.json index a2a47d615..d2d9d3a79 100644 --- a/data/sidebar_react_latest.json +++ b/data/sidebar_react_latest.json @@ -16,7 +16,8 @@ "styling", "router", "lazy-components", - "import-export-reactjs" + "import-export-reactjs", + "server-components" ], "Hooks & State Management": [ "hooks-overview", diff --git a/pages/docs/react/latest/server-components.mdx b/pages/docs/react/latest/server-components.mdx new file mode 100644 index 000000000..16f4bb64b --- /dev/null +++ b/pages/docs/react/latest/server-components.mdx @@ -0,0 +1,190 @@ +--- +title: Server Components +description: "Creating React components" +canonical: "/docs/react/latest/server-components" +--- + +# Server Components + +ReScript allows you to create server components as described in [React 19](https://react.dev/reference/rsc/server-components). + +## Server Functions + +To mark a file exposing functions as [Server Functions](https://react.dev/reference/rsc/server-functions), +you can use the `@@directive("'use server'")` tag. + + + +```res example +// src/actions/MyActions.res +@@directive("'use server'") + +let myHelper = () => "Hello from the server!" + +let helloWorld = async () => { + let response = myHelper() + response +} +``` +```js +'use server' +// Generated by ReScript, PLEASE EDIT WITH CARE + + +function myHelper() { + return "Hello from the server!"; +} + +async function helloWorld() { + return "Hello from the server!"; +} + +export { + myHelper, + helloWorld, +} +``` + + + +**Warning:** It is recommended to use an interface file here, to ensure only the functions you want to expose are exposed. + +```res +// src/actions/MyActions.resi +let helloWorld: unit => Promise +``` + +`myHelper` will remain unexported with this change. + +## Server Components + +[Server components](https://react.dev/reference/rsc/server-components) can be async. + + + +```res example +// src/pages/Index.res +@react.component +let make = async () => { + let data = await getData() + + +

{React.string("Hello from server component")}

+ + +} + +// Export as default +let default = make +``` +```js +'use server' +import * as JsxRuntime from "react/jsx-runtime"; + +async function make(param) { + await moo(); + return JsxRuntime.jsx("html", { + children: JsxRuntime.jsx("body", { + children: JsxRuntime.jsx("h1", { + children: "Hello from server component" + }) + }) + }); +} + +let Index = make; + +let make$1 = Index; + +let $$default = Index; + +export { + make$1 as make, + $$default as default, +} +``` + +
+ +A server function can be inlined in a server component and passed as prop to a client component. + + + +```res +@react.component +let make = async () => { + let data = await getData() + + let addData = + @directive("'use server'") + async (name: string) => { + // add to data store + true + } + + +

{React.string("Hello from server component")}

+ + + +} +``` +```js +async function make(param) { + await moo(); + let addData = async name => { + 'use server'; + return true; + }; + return JsxRuntime.jsx("html", { + children: JsxRuntime.jsxs("body", { + children: [ + JsxRuntime.jsx("h1", { + children: "Hello from server component" + }), + JsxRuntime.jsx(ClientComp, { + submit: addData + }) + ] + }) + }); +} +``` + +
+ +**Note** that when decorating the function with `@directive("'use server'")`, we use a single `@`, where to annotate an entire file we use `@@directive("'use server'")`. + +## Client Components + +[Client components](https://react.dev/reference/rsc/use-client) should use the `@@directive("'use client'")` attribute. + + + +```res +// src/components/ClientComp.res +@@directive("'use client'") + +@react.component +let make = (~submit) => +``` +```js +use client' +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as JsxRuntime from "react/jsx-runtime"; + +function ClientComp(props) { + return JsxRuntime.jsx("button", { + children: "yow from client" + }); +} + +let make = ClientComp; + +export { + make, +} +``` + + From 483f4141c7f7b204e83af15cdd530cadf426451b Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 28 Jul 2025 15:38:08 +0200 Subject: [PATCH 2/3] Remove moo --- pages/docs/react/latest/server-components.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pages/docs/react/latest/server-components.mdx b/pages/docs/react/latest/server-components.mdx index 16f4bb64b..cd4901a54 100644 --- a/pages/docs/react/latest/server-components.mdx +++ b/pages/docs/react/latest/server-components.mdx @@ -66,6 +66,7 @@ let helloWorld: unit => Promise // src/pages/Index.res @react.component let make = async () => { + // fetch some data from somewhere let data = await getData() @@ -82,7 +83,7 @@ let default = make import * as JsxRuntime from "react/jsx-runtime"; async function make(param) { - await moo(); + await getData(); return JsxRuntime.jsx("html", { children: JsxRuntime.jsx("body", { children: JsxRuntime.jsx("h1", { @@ -131,7 +132,7 @@ let make = async () => { ``` ```js async function make(param) { - await moo(); + await getData(); let addData = async name => { 'use server'; return true; From 8b8cfb92be9decbc36d063fa266aa26bb432ce52 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 28 Jul 2025 16:07:39 +0200 Subject: [PATCH 3/3] Ensure sample compiles --- pages/docs/react/latest/server-components.mdx | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/pages/docs/react/latest/server-components.mdx b/pages/docs/react/latest/server-components.mdx index cd4901a54..93621ab99 100644 --- a/pages/docs/react/latest/server-components.mdx +++ b/pages/docs/react/latest/server-components.mdx @@ -64,6 +64,9 @@ let helloWorld: unit => Promise ```res example // src/pages/Index.res +let data = [1, 2, 3] +let getData = () => Promise.resolve(data) + @react.component let make = async () => { // fetch some data from somewhere @@ -71,6 +74,9 @@ let make = async () => {

{React.string("Hello from server component")}

+
    + {data->Array.map(id =>
  • {React.int(id)}
  • )->React.array} +
} @@ -112,29 +118,55 @@ A server function can be inlined in a server component and passed as prop to a c ```res +module ClientComp = { + @react.component @module("some-module") + external make: (~submit: int => promise) => React.element = + "SomeClientComp" +} + +let data = [1, 2, 3] +let getData = () => Promise.resolve(data) + @react.component let make = async () => { let data = await getData() let addData = @directive("'use server'") - async (name: string) => { + async (id: int) => { // add to data store + data->Array.push(id) true }

{React.string("Hello from server component")}

+
    + {data->Array.map(id =>
  • {React.int(id)}
  • )->React.array} +
} ``` ```js +import * as SomeModule from "some-module"; + +let data = [ + 1, + 2, + 3 +]; + +function getData() { + return Promise.resolve(data); +} + async function make(param) { - await getData(); - let addData = async name => { + let data$1 = await Promise.resolve(data); + let addData = async id => { 'use server'; + data$1.push(id); return true; }; return JsxRuntime.jsx("html", { @@ -143,7 +175,12 @@ async function make(param) { JsxRuntime.jsx("h1", { children: "Hello from server component" }), - JsxRuntime.jsx(ClientComp, { + JsxRuntime.jsx("ul", { + children: data$1.map(id => JsxRuntime.jsx("li", { + children: id + })) + }), + JsxRuntime.jsx(SomeModule.SomeClientComp, { submit: addData }) ]