Parcel 14 ๐ฆ React Server Components โ react-server template & RSC support
React Server Components (RSC) support is Parcel’s most significant recent addition, introduced as beta in Parcel v2.14.0 . Unlike other RSC implementations that are tied to a full framework, Parcel’s approach is explicitly not a framework. It provides the bundler-level primitivesโthe "use client" and "use server" directives, the @parcel/rsc runtime library, and the unified module graph across server and client environmentsโand leaves the routing, server setup, and application structure to the developer . This gives teams the freedom to integrate RSC into an existing client-rendered app without a rewrite, or to build a server-rendered or statically-rendered app from scratch.
This chapter covers the three rendering modes Parcel supports (client-integrated, server-rendered, and static), the react-server template that scaffolds a TypeScript RSC project, the "use server-entry" directive that marks a page entry point, and the @parcel/rsc library that provides the server-side rendering and client-side hydration helpers.
Key point: Parcel is not a framework. It does not provide a router, a server, or a convention for file-based routing. It provides the bundler primitives that make RSC work, and the @parcel/rsc library that wraps the lower-level React APIs. The developer provides the server (Express, Fastify, etc.) and the routing logic.
Why Parcel’s RSC support is different
The framework problem. Most RSC implementations are embedded in a framework: Next.js, Remix, or a meta-framework that provides the server, the router, and the file conventions. The developer adopts the framework to get the RSC benefits. Parcel’s documentation states the philosophy directly: “Unlike other RSC implementations, Parcel is not a full framework. Instead, it provides the tools you need to build your own application or framework from scratch with much less work” .
The unified module graph problem. Parcel v2 has supported multiple environments (browser, node, web worker) since its initial release. The RSC support reuses this infrastructure: the server and client code are part of the same module graph, and the bundler can perform code splitting across environments . This is the technical foundation that makes the "use client" directive work: when the server environment imports a module with "use client", Parcel creates a client reference and a parallel client bundle, and the reference is serialized into the RSC Payload .
The incremental adoption problem. A team that already has a client-rendered React app can add a server target alongside the existing client target. Parcel builds both together and shares the common dependencies. The new feature that benefits from RSC can be rendered on the server, while the rest of the app remains client-rendered . The integration does not require a full migration.
a. The three rendering modes
Parcel supports three modes, and the choice depends on whether the page contains dynamic data that is personalized for the user.
Client-integrated RSC. The existing client-rendered app continues to work as before. A new server target is added, and the server renders specific routes to an RSC Payload. The client fetches the payload and renders it inside a <Suspense> boundary. This mode is for adding RSC to an existing SPA without rewriting it .
Server-rendered RSC. The server is the entry point of the Parcel build, not an HTML file. The server renders the page to an RSC Payload as part of the initial request, which avoids the network waterfall of a client-rendered app. Parcel v2.14.0 includes out-of-the-box support for running the Node server in development mode: parcel src/server.js builds the server and runs it with hot reloading .
Static rendering. The page is pre-rendered to fully static HTML at build time. A marketing page or a blog post that does not contain personalized data is a candidate. The static HTML is served from a CDN, and the interactive Client Components are hydrated in the browser. Parcel includes a built-in static site generator powered by RSC, and the react-static target name is the configuration mechanism .
The three modes can be mixed in the same app by creating multiple targets. A marketing page can be statically rendered to a CDN, and a product dashboard can be server-rendered .
b. The react-server template
The react-server template scaffolds a TypeScript RSC application. The create-parcel CLI is the tool that generates the project.
npm create parcel react-server my-rsc-app
The template creates a project with the client and server targets already configured, the server entry point, and the "use server-entry" directive in the page component . The template is the fastest way to see the RSC setup in action, and it is the starting point for a new project that wants the server-rendered mode.
The package.json targets are the core of the configuration. The client target points at the HTML file, and the server target points at the server code. Parcel builds both together .
{
"targets": {
"client": { "source": "index.html" },
"server": { "source": "src/server.js" }
}
}
The react-static target name is the configuration for the static mode. The source points at a directory of page components, and each component is rendered to an HTML file in the dist directory .
c. The "use server-entry" directive and the @parcel/rsc library
The "use server-entry" directive is Parcel-specific. It marks a server component as the entry point of a page or route, and it creates a code-splitting boundary. The dependencies referenced by the route are bundled together, and the shared dependencies between pages are placed in a shared bundle . The directive is the mechanism that tells Parcel which server component is the root of the page.
The @parcel/rsc library wraps the lower-level React APIs. The server-side renderRequest function renders a component to an RSC Payload and sends it as the response. The client-side hydrate function hydrates the payload, and the fetchRSC function fetches a new payload for a client-side navigation .
// server.js
import express from 'express'
import { renderRequest } from '@parcel/rsc/node'
import { Page } from './Page'
const app = express()
app.get('/', async (req, res) => {
await renderRequest(req, res, <Page />, { component: Page })
})
app.listen(3000)
// Page.jsx
'use server-entry'
export function Page() {
return <h1>Hello</h1>
}
The "use server" directive is supported at the top of a file, not inline within a function. A file with "use server" exports async functions that the client can call. The setServerCallback function in @parcel/rsc/client defines the HTTP request that is made when a Server Function is called, and the server handles the POST request and calls the original function .
Complete Example Session
# ============================================
# PART 1: THE TEMPLATE
# ============================================
npm create parcel react-server my-rsc-app
cd my-rsc-app
// ============================================
// PART 2: THE TARGETS
// ============================================
{
"targets": {
"client": { "source": "index.html" },
"server": { "source": "src/server.js" }
}
}
// ============================================
// PART 3: THE SERVER COMPONENT
// ============================================
// src/Page.jsx
'use server-entry'
export function Page() {
return <h1>Hello, RSC</h1>
}
// ============================================
// PART 4: THE SERVER
// ============================================
// src/server.js
import express from 'express'
import { renderRequest } from '@parcel/rsc/node'
import { Page } from './Page'
const app = express()
app.get('/', async (req, res) => {
await renderRequest(req, res, <Page />, { component: Page })
})
app.listen(3000)
// ============================================
// PART 5: THE CLIENT COMPONENT
// ============================================
// src/LikeButton.jsx
'use client'
import { useState } from 'react'
export function LikeButton() {
const [liked, setLiked] = useState(false)
return <button onClick={() => setLiked(!liked)}>{liked ? 'Liked' : 'Like'}</button>
}
// ============================================
// PART 6: THE SERVER COMPONENT IMPORTING CLIENT
// ============================================
// src/Page.jsx
'use server-entry'
import { LikeButton } from './LikeButton'
export function Page() {
return (
<div>
<h1>Hello</h1>
<LikeButton />
</div>
)
}
// ============================================
// PART 7: THE CLIENT FETCH
// ============================================
// src/client.js
import { hydrate, fetchRSC } from '@parcel/rsc/client'
let updateRoot = hydrate()
async function navigate(pathname) {
let root = await fetchRSC(pathname)
updateRoot(root)
}
// ============================================
// PART 8: THE SERVER FUNCTION
// ============================================
// src/actions.js
'use server'
export async function likeComment(id) {
await db.comment.update({ where: { id }, data: { likes: { increment: 1 } } })
}
// ============================================
// PART 9: THE SERVER CALLBACK
// ============================================
// src/client.js
import { setServerCallback } from '@parcel/rsc/client'
setServerCallback(async (id, args) => {
const res = await fetch('/api', {
method: 'POST',
headers: { 'X-Server-Action-Id': id },
body: JSON.stringify(args),
})
return res.json()
})
# ============================================
# PART 10: THE STATIC MODE
# ============================================
# package.json
# "react-static": { "source": "pages/**/*.tsx" }
# parcel build
The ten parts covered the template, the targets, the server component, the server, the client component, the import, the client fetch, the Server Function, the server callback, and the static mode.
Quick Reference
Rendering modes
| Mode | Entry point | Use case |
|---|---|---|
| Client-integrated | Existing HTML + server target | Add RSC to an SPA |
| Server-rendered | Server code | Dynamic pages |
| Static | pages/**/*.tsx with react-static | Marketing, blog |
The directives
| Directive | Location | Purpose |
|---|---|---|
"use client" | Top of file | Mark a client component |
"use server" | Top of file | Mark a Server Function file |
"use server-entry" | Top of file | Mark a page entry point (Parcel-specific) |
The @parcel/rsc library
| Function | Context | Purpose |
|---|---|---|
renderRequest | Server | Render to RSC Payload |
hydrate | Client | Hydrate the payload |
fetchRSC | Client | Fetch a new payload for navigation |
setServerCallback | Client | Define the Server Function transport |
The targets
| Target | Source | Output |
|---|---|---|
client | HTML file | Client bundles |
server | Server code | Server bundle |
react-static | pages/**/*.tsx | Static HTML |
Best Practices
โ Do This:
// Use "use server-entry" on the page component
'use server-entry' // โ
// Use "use client" on the interactive component
'use client' // โ
// Use the @parcel/rsc helpers for rendering and hydration
import { renderRequest } from '@parcel/rsc/node' // โ
// Use the react-static target for static pages
{ "react-static": { "source": "pages/**/*.tsx" } } // โ
โ Don’t Do This:
// Use "use server" inline in a function
export function Page() { 'use server'; ... } // โ (top of file only)
// Expect a router or a file convention
// Parcel is not a framework // โ
// Import the client component without "use client"
import { LikeButton } from './LikeButton' // โ (if it uses hooks)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Server component runs on the client | Missing "use server-entry" | Add the directive |
| Client component does not hydrate | Missing "use client" | Add the directive |
| Server Function is not callable | Missing setServerCallback | Define the transport |
| Static page is not generated | Wrong target name | Use react-static |
Build fails on @parcel/rsc import | Package not installed | Install @parcel/rsc |
Real-World Examples
1. The template
npm create parcel react-server my-rsc-app
2. The server entry
'use server-entry'
export function Page() { return <h1>Hello</h1> }
3. The client component
'use client'
export function Button() { const [n, setN] = useState(0); ... }
4. The server render
await renderRequest(req, res, <Page />, { component: Page })
5. The client hydrate
let updateRoot = hydrate()
6. The client fetch
let root = await fetchRSC(pathname)
7. The Server Function
'use server'
export async function like(id) { ... }
8. The server callback
setServerCallback(async (id, args) => { ... })
9. The static target
{ "react-static": { "source": "pages/**/*.tsx" } }
10. The decision
SPA integration โ client target + server target
Dynamic pages โ server-rendered
Static pages โ react-static target
Visual
The three modes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLIENT-INTEGRATED โ
โ Existing SPA + new server target โ
โ Client fetches RSC Payload for specific routes โ
โ โ
โ SERVER-RENDERED โ
โ Server is the entry point โ
โ RSC Payload rendered per request โ
โ โ
โ STATIC โ
โ react-static target โ
โ HTML generated at build time โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The directive flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Page.jsx โ
โ "use server-entry" โ
โ โ โ
โ โผ โ
โ Parcel creates a code-splitting boundary โ
โ โ โ
โ โโโ Server dependencies โ server bundle โ
โ โ โ
โ โโโ "use client" imports โ client bundle โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The server rendering
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Express server โ
โ app.get('/', async (req, res) => { โ
โ await renderRequest(req, res, <Page />, { component: Page })โ
โ }) โ
โ โ
โ Page is a Server Component. โ
โ renderRequest produces the RSC Payload. โ
โ The client hydrates the payload. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The static mode
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ pages/index.tsx โ
โ pages/about.tsx โ
โ โ โ
โ โผ โ
โ parcel build (react-static target) โ
โ โ โ
โ โผ โ
โ dist/index.html โ
โ dist/about.html โ
โ โ
โ Served from a CDN, hydrated in the browser. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| RSC support introduced | Parcel v2.14.0 (beta) |
| Parcel’s approach | Bundler primitives, not a framework |
| Template | react-server |
| Entry directive | "use server-entry" |
| Client directive | "use client" |
| Server directive | "use server" (top of file) |
| Runtime library | @parcel/rsc |
| Rendering modes | Client-integrated, server-rendered, static |
| Static target | react-static |
| Server dev support | parcel src/server.js with hot reload |
Key takeaways:
- Parcel is not a framework. It provides the bundler primitives and the
@parcel/rsclibrary, and leaves the server, routing, and application structure to the developer. This is a deliberate design choice that enables incremental adoption and integration into existing apps . - The
react-servertemplate scaffolds a TypeScript RSC project. It creates the client and server targets, the server entry point, and the"use server-entry"directive in the page component. It is the fastest way to see the setup in action . - The
"use server-entry"directive is Parcel-specific. It marks a server component as the entry point of a page, creating a code-splitting boundary and enabling the shared dependency extraction between pages . - The three rendering modes cover different use cases. Client-integrated RSC adds server rendering to an existing SPA. Server-rendered RSC makes the server the entry point and renders per request. Static rendering pre-renders to HTML at build time for pages that do not contain personalized data .
- The
"use client"and"use server"directives are the standard React directives. The"use client"marks a client component, and the"use server"marks a file of Server Functions. Parcel supports"use server"at the top of a file, not inline within a function . - The
@parcel/rsclibrary wraps the lower-level React APIs.renderRequestrenders a component to an RSC Payload on the server.hydratehydrates the payload on the client.fetchRSCfetches a new payload for a client-side navigation.setServerCallbackdefines the transport for Server Functions .
Remember: Parcel’s React Server Components support is a set of bundler primitives, not a framework. The react-server template is the starting point. The "use server-entry" directive marks the page entry point. The "use client" and "use server" directives are the standard React boundary markers. The @parcel/rsc library provides the rendering and hydration helpers. And the three modesโclient-integrated, server-rendered, and staticโallow the same app to mix pages that need different rendering strategies.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!