Skip to content

Add page on server components #1079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/sidebar_react_latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"styling",
"router",
"lazy-components",
"import-export-reactjs"
"import-export-reactjs",
"server-components"
],
"Hooks & State Management": [
"hooks-overview",
Expand Down
228 changes: 228 additions & 0 deletions pages/docs/react/latest/server-components.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
---
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.

<CodeTab labels={["ReScript", "JS Output"]}>

```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,
}
```

</CodeTab>

**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<string>
```

`myHelper` will remain unexported with this change.

## Server Components

[Server components](https://react.dev/reference/rsc/server-components) can be async.

<CodeTab labels={["ReScript", "JS Output"]}>

```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
let data = await getData()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be cool if we could actually do something with data

<html>
<body>
<h1> {React.string("Hello from server component")} </h1>
<ul>
{data->Array.map(id => <li> {React.int(id)} </li>)->React.array}
</ul>
</body>
</html>
}

// Export as default
let default = make
```
```js
'use server'
import * as JsxRuntime from "react/jsx-runtime";

async function make(param) {
await getData();
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,
}
```

</CodeTab>

A server function can be inlined in a server component and passed as prop to a client component.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
module ClientComp = {
@react.component @module("some-module")
external make: (~submit: int => promise<bool>) => 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 (id: int) => {
// add to data store
data->Array.push(id)
true
}
<html>
<body>
<h1> {React.string("Hello from server component")} </h1>
<ul>
{data->Array.map(id => <li> {React.int(id)} </li>)->React.array}
</ul>
<ClientComp submit={addData} />
</body>
</html>
}
```
```js
import * as SomeModule from "some-module";

let data = [
1,
2,
3
];

function getData() {
return Promise.resolve(data);
}

async function make(param) {
let data$1 = await Promise.resolve(data);
let addData = async id => {
'use server';
data$1.push(id);
return true;
};
return JsxRuntime.jsx("html", {
children: JsxRuntime.jsxs("body", {
children: [
JsxRuntime.jsx("h1", {
children: "Hello from server component"
}),
JsxRuntime.jsx("ul", {
children: data$1.map(id => JsxRuntime.jsx("li", {
children: id
}))
}),
JsxRuntime.jsx(SomeModule.SomeClientComp, {
submit: addData
})
]
})
});
}
```

</CodeTab>

**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.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
// src/components/ClientComp.res
@@directive("'use client'")

@react.component
let make = (~submit) => <button> {React.string("yow from client")} </button>
```
```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,
}
```

</CodeTab>