https://github.com/jonathanhefner/next-remote-components
https://github.com/jonathanhefner/next-remote-components
Last synced: 8 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/jonathanhefner/next-remote-components
- Owner: jonathanhefner
- License: mit
- Created: 2024-12-05T19:08:03.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-03-31T17:03:43.000Z (over 1 year ago)
- Last Synced: 2025-03-31T18:23:26.950Z (over 1 year ago)
- Language: TypeScript
- Homepage: https://next-remote-components.vercel.app
- Size: 199 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# next-remote-components
This is a userland prototype and demo of remote components — React server components that are rendered remotely, on demand by client components, without the need for an explicit fetch or dedicated API endpoint.
See it in action at https://next-remote-components.vercel.app/.
The core implementation is in [`lib/rrc.tsx`](./lib/rrc.tsx).
My hope is that React itself will eventually implement this functionality. In the mean time, this project serves as a proof of concept.
## Usage
To use remote components in your own project, copy [`lib/rrc.tsx`](./lib/rrc.tsx) into your project, and then follow these steps:
1. Create a server function that returns a component:
```tsx
// @/lib/server-functions.tsx
'use server'
export async function getMyRemoteComponent() {
return Hello from the server!
}
```
2. Define a remote component via the `remote()` helper:
```tsx
// @/lib/client-components.tsx
'use client'
import { getMyRemoteComponent } from "@/lib/server-functions"
import { remote } from "@/lib/rrc"
const MyRemoteComponent = remote(getMyRemoteComponent)
```
3. Create a client component that uses the remote component wrapped in a `` boundary:
```tsx
// @/lib/client-components.tsx
'use client'
import { getMyRemoteComponent } from "@/lib/server-functions"
import { remote, RemoteSuspense } from "@/lib/rrc"
const MyRemoteComponent = remote(getMyRemoteComponent)
export function MyClientComponent() {
return (
The server says:
)
}
```
## Features
### Static typing
Remote components and their props are statically typed. For example:
```tsx
export async function getMyRemoteComponent(props: { value: number }) {
return <>>
}
```
```tsx
const MyRemoteComponent = remote(getMyRemoteComponent)
export function MyClientComponent() {
return (
{/* ERROR: Property 'value' is missing in type '{}' but required in type '{ value: number; }' */}
{/* ERROR: Type 'string' is not assignable to type 'number' */}
{/* ERROR: Type '{ children: string; value: number; }' is not assignable to type 'IntrinsicAttributes & { value: number; }' */}
child
{/* CORRECT! */}
)
}
```
### Client components as children
Remote components support "passing" client components as children. Behind the scenes, the remote components render a slot which is then filled with the given children. For example:
```tsx
export async function getMyRemoteComponent({ children }: { children: React.ReactNode }) {
return <>
I have twins:
- {children}
- {children}
>
}
```
```tsx
const MyRemoteComponent = remote(getMyRemoteComponent)
export function MyClientComponent() {
return (
child
)
}
```
## Caveats
### Props must be serializable
Remote components props are serialized in the same way as server function arguments. Thus, props values must be one of the [supported types](https://19.react.dev/reference/rsc/use-server#serializable-parameters-and-return-values).
### Memoization and rerenders
Remote components are memoized using React's [`memo`](https://19.react.dev/reference/react/memo), with special casing when passing children. Thus, a remote component will rerender when a prop value changes according to [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is).
In the future, the `remote()` helper might accept additional arguments to customize this behavior.
## Further improvements from a non-userland implementation
### DX improvements
The behavior of `` could be folded into React's built-in `` boundary. Thus, developers could simply use `` as they would elsewhere.
### Performance improvements
Remote components rely on server functions which, currently, are [not recommended for data fetching](https://19.react.dev/reference/rsc/use-server#caveats) because they run sequentially and are not cacheable. However, in the future, there may be variant of server functions which _are_ designed for data fetching. One proposal is to special case server functions based on naming convention, such that functions that start with `get...` (e.g. `getData()`) are treated as data fetching functions.