Skip to content

Commit b94e738

Browse files
authored
Add page on server components (#1079)
* Add page on server components * Remove moo * Ensure sample compiles
1 parent 4a12f92 commit b94e738

File tree

2 files changed

+230
-1
lines changed

2 files changed

+230
-1
lines changed

data/sidebar_react_latest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"styling",
1717
"router",
1818
"lazy-components",
19-
"import-export-reactjs"
19+
"import-export-reactjs",
20+
"server-components"
2021
],
2122
"Hooks & State Management": [
2223
"hooks-overview",
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
title: Server Components
3+
description: "Creating React components"
4+
canonical: "/docs/react/latest/server-components"
5+
---
6+
7+
# Server Components
8+
9+
ReScript allows you to create server components as described in [React 19](https://react.dev/reference/rsc/server-components).
10+
11+
## Server Functions
12+
13+
To mark a file exposing functions as [Server Functions](https://react.dev/reference/rsc/server-functions),
14+
you can use the `@@directive("'use server'")` tag.
15+
16+
<CodeTab labels={["ReScript", "JS Output"]}>
17+
18+
```res example
19+
// src/actions/MyActions.res
20+
@@directive("'use server'")
21+
22+
let myHelper = () => "Hello from the server!"
23+
24+
let helloWorld = async () => {
25+
let response = myHelper()
26+
response
27+
}
28+
```
29+
```js
30+
'use server'
31+
// Generated by ReScript, PLEASE EDIT WITH CARE
32+
33+
34+
function myHelper() {
35+
return "Hello from the server!";
36+
}
37+
38+
async function helloWorld() {
39+
return "Hello from the server!";
40+
}
41+
42+
export {
43+
myHelper,
44+
helloWorld,
45+
}
46+
```
47+
48+
</CodeTab>
49+
50+
**Warning:** It is recommended to use an interface file here, to ensure only the functions you want to expose are exposed.
51+
52+
```res
53+
// src/actions/MyActions.resi
54+
let helloWorld: unit => Promise<string>
55+
```
56+
57+
`myHelper` will remain unexported with this change.
58+
59+
## Server Components
60+
61+
[Server components](https://react.dev/reference/rsc/server-components) can be async.
62+
63+
<CodeTab labels={["ReScript", "JS Output"]}>
64+
65+
```res example
66+
// src/pages/Index.res
67+
let data = [1, 2, 3]
68+
let getData = () => Promise.resolve(data)
69+
70+
@react.component
71+
let make = async () => {
72+
// fetch some data from somewhere
73+
let data = await getData()
74+
<html>
75+
<body>
76+
<h1> {React.string("Hello from server component")} </h1>
77+
<ul>
78+
{data->Array.map(id => <li> {React.int(id)} </li>)->React.array}
79+
</ul>
80+
</body>
81+
</html>
82+
}
83+
84+
// Export as default
85+
let default = make
86+
```
87+
```js
88+
'use server'
89+
import * as JsxRuntime from "react/jsx-runtime";
90+
91+
async function make(param) {
92+
await getData();
93+
return JsxRuntime.jsx("html", {
94+
children: JsxRuntime.jsx("body", {
95+
children: JsxRuntime.jsx("h1", {
96+
children: "Hello from server component"
97+
})
98+
})
99+
});
100+
}
101+
102+
let Index = make;
103+
104+
let make$1 = Index;
105+
106+
let $$default = Index;
107+
108+
export {
109+
make$1 as make,
110+
$$default as default,
111+
}
112+
```
113+
114+
</CodeTab>
115+
116+
A server function can be inlined in a server component and passed as prop to a client component.
117+
118+
<CodeTab labels={["ReScript", "JS Output"]}>
119+
120+
```res
121+
module ClientComp = {
122+
@react.component @module("some-module")
123+
external make: (~submit: int => promise<bool>) => React.element =
124+
"SomeClientComp"
125+
}
126+
127+
let data = [1, 2, 3]
128+
let getData = () => Promise.resolve(data)
129+
130+
@react.component
131+
let make = async () => {
132+
let data = await getData()
133+
134+
let addData =
135+
@directive("'use server'")
136+
async (id: int) => {
137+
// add to data store
138+
data->Array.push(id)
139+
true
140+
}
141+
<html>
142+
<body>
143+
<h1> {React.string("Hello from server component")} </h1>
144+
<ul>
145+
{data->Array.map(id => <li> {React.int(id)} </li>)->React.array}
146+
</ul>
147+
<ClientComp submit={addData} />
148+
</body>
149+
</html>
150+
}
151+
```
152+
```js
153+
import * as SomeModule from "some-module";
154+
155+
let data = [
156+
1,
157+
2,
158+
3
159+
];
160+
161+
function getData() {
162+
return Promise.resolve(data);
163+
}
164+
165+
async function make(param) {
166+
let data$1 = await Promise.resolve(data);
167+
let addData = async id => {
168+
'use server';
169+
data$1.push(id);
170+
return true;
171+
};
172+
return JsxRuntime.jsx("html", {
173+
children: JsxRuntime.jsxs("body", {
174+
children: [
175+
JsxRuntime.jsx("h1", {
176+
children: "Hello from server component"
177+
}),
178+
JsxRuntime.jsx("ul", {
179+
children: data$1.map(id => JsxRuntime.jsx("li", {
180+
children: id
181+
}))
182+
}),
183+
JsxRuntime.jsx(SomeModule.SomeClientComp, {
184+
submit: addData
185+
})
186+
]
187+
})
188+
});
189+
}
190+
```
191+
192+
</CodeTab>
193+
194+
**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'")`.
195+
196+
## Client Components
197+
198+
[Client components](https://react.dev/reference/rsc/use-client) should use the `@@directive("'use client'")` attribute.
199+
200+
<CodeTab labels={["ReScript", "JS Output"]}>
201+
202+
```res
203+
// src/components/ClientComp.res
204+
@@directive("'use client'")
205+
206+
@react.component
207+
let make = (~submit) => <button> {React.string("yow from client")} </button>
208+
```
209+
```js
210+
use client'
211+
// Generated by ReScript, PLEASE EDIT WITH CARE
212+
213+
import * as JsxRuntime from "react/jsx-runtime";
214+
215+
function ClientComp(props) {
216+
return JsxRuntime.jsx("button", {
217+
children: "yow from client"
218+
});
219+
}
220+
221+
let make = ClientComp;
222+
223+
export {
224+
make,
225+
}
226+
```
227+
228+
</CodeTab>

0 commit comments

Comments
 (0)