React Server Components 06 🧬 The No-Directive Rule — Why Server Components have no “use server” marker
A React Server Component does not carry a "use server" directive. There is no marker that says “this component runs on the server,” because the server is the default. In an RSC-enabled framework, every component is a Server Component unless something marks it as a Client Component. The "use client" directive is the boundary marker; the absence of it is what makes a component a Server Component. Confusing "use server" with a component marker is the most common conceptual error in the RSC model, and it produces code that either fails to compile or silently does the wrong thing.
This chapter covers the asymmetry between the two directives: why "use client" exists as a boundary and why "use server" is reserved for Server Functions rather than components. It explains the default-server rendering model, the module dependency tree that determines where a component is evaluated, and the specific purpose of "use server" as a callable-endpoint marker. By the end, you will understand why a Server Component file contains no directive at all, and why adding one is a category error.
Key point: "use client" marks a boundary in the module dependency tree. "use server" marks a function as callable from the client. Neither directive marks a component as “server-side,” because server-side is the default.
Why the asymmetry exists
The default-server problem. In an application that uses React Server Components, the framework renders the root component on the server by default. It walks the module dependency tree and evaluates every module that does not have a "use client" directive on the server. A component defined in such a module is a Server Component by virtue of where it is evaluated, not by virtue of a marker . There is nothing to declare because there is no opt-in.
The boundary problem. "use client" exists because the client is not the default. When a module needs state, event handlers, or browser APIs, it must be marked so that the bundler creates a client boundary. Everything imported from that module, and everything transitively imported by it, becomes client code. The directive is a boundary in the module graph, not a label on a single component . A Server Component has no equivalent boundary because it is already on the server side of the graph.
The callable-endpoint problem. "use server" exists for a different reason entirely. It does not mark a component; it marks an async function as a Server Function that the client is allowed to call over the network. When a client invokes a Server Function, React serializes the arguments, sends a POST request to the server, executes the function, and serializes the return value back to the client . The directive is a security and bundling declaration: this function is an endpoint, and its implementation stays on the server. Applying it to a component would not make the component a Server Component; it would attempt to expose the component as a callable endpoint, which is not what components are for.
a. The default: no directive means server
A file without either directive is evaluated on the server. A function defined in that file and used as a component is a Server Component.
// app/components/header.tsx
// No directive. This is a Server Component.
export function Header() {
return <header>My App</header>
}
The component can read from the filesystem, query a database, or call an internal service. Its code is never sent to the browser. The rendered output is sent as part of the RSC payload, and the client renders that output without downloading the component’s implementation .
The module dependency tree determines the outcome. If header.tsx is imported by a Server Component, the usage is server-rendered. If the same file is imported by a module with "use client", the usage becomes a Client Component, even though the file itself has no directive . The directive is on the importer, not the imported.
b. The boundary: “use client” marks the client side
"use client" is placed at the top of a file. It marks that module and its transitive dependencies as client modules. The bundler includes them in the client bundle, and the framework evaluates them in the browser.
// app/components/counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
The directive is required because useState and onClick are client-only features. Without the directive, the component would be evaluated on the server, where hooks and event handlers do not work . The directive is not saying “this is a Client Component” as a label; it is saying “start the client boundary here.”
A Client Component can render a Server Component if the Server Component is passed as a child. The Client Component receives the already-rendered JSX as a prop, and the Server Component’s code never enters the client bundle . This is why the module graph, not the render tree, determines the boundary.
c. The exception: “use server” is for functions, not components
"use server" marks an async function as a Server Function. The function can be called from client code, and the call becomes a network request.
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
await db.post.create({ data: { title: formData.get('title') } })
}
When a Client Component imports createPost and passes it to a <form action>, React sets up the endpoint. The form submission sends the FormData to the server, the function runs, and the result is returned . The function is not a component. It returns a value, not JSX. It is called by the client, not rendered by the server.
If "use server" were placed at the top of a component file, the framework would treat the exported functions as Server Functions. A component that returns JSX is not a valid Server Function, because JSX is not a serializable return value for a network call . The code would fail or behave unpredictably. The directive is not a component marker; it is a function marker.
Complete Example Session
// ============================================
// PART 1: A SERVER COMPONENT HAS NO DIRECTIVE
// ============================================
// app/components/product-list.tsx
// No "use server" and no "use client"
export async function ProductList() {
const products = await db.product.findMany()
return <ul>{products.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
}
// The component is evaluated on the server by default.
// ============================================
// PART 2: THE MODULE GRAPH DETERMINES THE ENVIRONMENT
// ============================================
// app/page.tsx imports ProductList
import { ProductList } from './components/product-list'
export default function Page() {
return <ProductList />
}
// ProductList is imported by a module with no directive.
// It is evaluated on the server.
// ============================================
// PART 3: "use client" CREATES THE BOUNDARY
// ============================================
// app/components/counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [n, setN] = useState(0)
return <button onClick={() => setN(n + 1)}>{n}</button>
}
// The directive starts the client boundary.
// Everything below it in the module graph is client code.
// ============================================
// PART 4: A SERVER COMPONENT PASSED AS CHILDREN
// ============================================
// app/components/client-wrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
// app/page.tsx
import { ClientWrapper } from './components/client-wrapper'
import { ProductList } from './components/product-list'
export default function Page() {
return (
<ClientWrapper>
<ProductList />
</ClientWrapper>
)
}
// ProductList is a Server Component rendered as children.
// Its code never enters the client bundle.
// ============================================
// PART 5: "use server" MARKS A FUNCTION
// ============================================
// app/actions.ts
'use server'
export async function deleteProduct(id: string) {
await db.product.delete({ where: { id } })
revalidatePath('/products')
}
// The function is callable from the client.
// It is not a component.
// ============================================
// PART 6: CALLING THE SERVER FUNCTION
// ============================================
// app/components/delete-button.tsx
'use client'
import { deleteProduct } from '../actions'
export function DeleteButton({ id }: { id: string }) {
return <button onClick={() => deleteProduct(id)}>Delete</button>
}
// The import crosses the client-server boundary.
// The call becomes a network request.
// ============================================
// PART 7: THE WRONG USE OF "use server"
// ============================================
// app/components/bad-component.tsx
'use server'
export function BadComponent() {
return <div>This will not work as intended</div>
}
// A component is not a Server Function.
// The directive is the wrong marker.
// ============================================
// PART 8: THE CORRECT VERSION
// ============================================
// app/components/good-component.tsx
export function GoodComponent() {
return <div>This is a Server Component by default</div>
}
// No directive is needed.
// ============================================
// PART 9: THE DECISION TREE
// ============================================
// Does the component need useState, useEffect, or onClick?
// Yes → add "use client"
// No → add nothing (Server Component)
//
// Does the function mutate data and need to be called from the client?
// Yes → add "use server"
// No → add nothing
// ============================================
// PART 10: THE COMPLETE PICTURE
// ============================================
// Server Component: no directive
// Client Component: "use client"
// Server Function: "use server"
//
// The three categories are distinct.
// No component carries "use server".
The ten parts covered the directive-free Server Component, the module graph, the client boundary, passing Server Components as children, the Server Function marker, calling the function, the incorrect use of "use server" on a component, the correct version, the decision tree, and the complete picture.
Quick Reference
Directive mapping
| Category | Directive | Location | Purpose |
|---|---|---|---|
| Server Component | None | — | Default server rendering |
| Client Component | "use client" | Top of file | Start client boundary |
| Server Function | "use server" | Top of async function or file | Mark callable endpoint |
What "use server" is not
| Misconception | Reality |
|---|---|
| Marks a component as server-side | Components are server-side by default |
| Required for async components | Async Server Components need no directive |
| Makes a component callable from the client | Components are rendered, not called |
| Needed for data fetching | Server Components fetch directly |
Where directives go
| Directive | Placement | Example |
|---|---|---|
"use client" | First line of file | 'use client' at top of counter.tsx |
"use server" | First line of async function body | Inside createPost |
"use server" | First line of file | At top of actions.ts (all exports) |
Best Practices
✅ Do This:
// Leave Server Components without a directive
export function Header() { return <header /> } // ✅
// Add "use client" only when the component needs client features
'use client'
import { useState } from 'react' // ✅
// Add "use server" to async functions that mutate data
'use server'
export async function create(formData: FormData) { ... } // ✅
// Pass Server Components as children to Client Components
<ClientWrapper><ServerComponent /></ClientWrapper> // ✅
❌ Don’t Do This:
// Add "use server" to a component file
'use server'
export function MyComponent() { return <div /> } // ❌
// Add "use client" to a component that has no client needs
'use client'
export function StaticText() { return <p>Hello</p> } // ❌
// Import a Server Component directly into a Client Component
'use client'
import { ServerComponent } from './server' // ❌ (becomes client code)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
Component fails with "use server" | Directive marks it as a Server Function | Remove the directive; Server Components need none |
| Server Component becomes client code | Imported by a "use client" module | Pass as children instead of importing |
| Async component does not work | Assumed "use server" was required | Async components are Server Components by default |
useState fails in a component | No "use client" directive | Add the directive at the top of the file |
| Server Function cannot be called | No "use server" marker | Add the directive to the async function or file |
Real-World Examples
1. Server Component (no directive)
export async function PostList() {
const posts = await db.post.findMany()
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
}
2. Client Component (“use client”)
'use client'
export function LikeButton() {
const [liked, setLiked] = useState(false)
return <button onClick={() => setLiked(!liked)}>Like</button>
}
3. Server Function (“use server”)
'use server'
export async function createPost(formData: FormData) {
await db.post.create({ data: { title: formData.get('title') } })
}
4. Passing Server Component as children
<ClientWrapper>
<PostList />
</ClientWrapper>
5. Async Server Component
export async function UserProfile({ id }: { id: string }) {
const user = await db.user.findUnique({ where: { id } })
return <div>{user.name}</div>
}
6. Server Function in a form
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
7. Client Component boundary
'use client'
import { useState } from 'react'
// Everything below is client code
8. Wrong directive on a component
'use server' // Wrong: this is not a Server Function
export function Bad() { return <div /> }
9. Correct directive placement
'use server'
export async function deleteItem(id: string) { ... }
10. Decision tree
// Needs state? → "use client"
// Needs mutation callable from client? → "use server"
// Otherwise → no directive
Visual
The three categories
┌─────────────────────────────────────────────────────────────┐
│ SERVER COMPONENT │
│ Directive: none │
│ Rendered: server │
│ Code sent to client: no (output only) │
│ Can use: async, DB, filesystem │
│ │
│ CLIENT COMPONENT │
│ Directive: "use client" │
│ Rendered: client (and server for initial HTML) │
│ Code sent to client: yes │
│ Can use: state, effects, event handlers │
│ │
│ SERVER FUNCTION │
│ Directive: "use server" │
│ Called: client → server over network │
│ Code sent to client: no (endpoint reference only) │
│ Purpose: mutations │
└─────────────────────────────────────────────────────────────┘
The module dependency tree
┌─────────────────────────────────────────────────────────────┐
│ MODULE GRAPH │
│ │
│ app/page.tsx (no directive) │
│ │ │
│ ├── imports PostList (no directive) │
│ │ → Server Component │
│ │ │
│ └── imports ClientWrapper ("use client") │
│ → Client boundary starts here │
│ → ClientWrapper is a Client Component │
│ │
│ The directive is on the module, not the component usage. │
└─────────────────────────────────────────────────────────────┘
The wrong mental model vs the right one
┌─────────────────────────────────────────────────────────────┐
│ WRONG │
│ │
│ "This component runs on the server, │
│ so it needs 'use server'." │
│ │
│ Server Component file: 'use server' │
│ Result: treated as a Server Function endpoint, fails │
│ │
│ ───────────────────────────────────────────────────────────│
│ │
│ RIGHT │
│ │
│ "Components run on the server by default. │
│ I only mark the client boundary." │
│ │
│ Server Component file: no directive │
│ Client Component file: 'use client' │
│ Server Function: 'use server' │
└─────────────────────────────────────────────────────────────┘
Server Function call flow
┌─────────────────────────────────────────────────────────────┐
│ "use server" FUNCTION CALL │
│ │
│ Client Component │
│ │ │
│ ├── calls createPost(formData) │
│ │ │
│ ▼ │
│ React serializes arguments │
│ │ │
│ ▼ │
│ POST request to server │
│ │ │
│ ▼ │
│ Function executes on server │
│ │ │
│ ▼ │
│ Return value serialized │
│ │ │
│ ▼ │
│ Client receives result │
└─────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Server Component directive | None |
| Client Component directive | "use client" |
| Server Function directive | "use server" |
| Default rendering environment | Server |
"use client" scope | Module boundary (file and transitive imports) |
"use server" scope | Async function or file exports |
"use server" on a component | Incorrect; component is not a callable function |
| Directive placement | First line, before imports |
Key takeaways:
- Server Components have no directive because server is the default. In an RSC application, every component is server-rendered unless a
"use client"boundary marks it otherwise. The absence of a directive is the marker . "use client"is a boundary, not a label. It starts a client module subtree in the dependency graph. Everything imported from that module becomes client code. The directive is about where code is evaluated, not what kind of component it is ."use server"is for functions, not components. It marks an async function as callable from the client over the network. A component that returns JSX is not a valid Server Function, and applying the directive to one is a category error .- Async Server Components need no directive. The
asynckeyword and theawaitinside the component work because the component is already evaluated on the server. No marker is required or appropriate. - The module graph determines the environment. A component defined in a file without a directive is server-rendered when imported by server code. If the same file is imported by a
"use client"module, the usage becomes client-rendered . The directive is on the importer, not the component. - The three categories are distinct. Server Component, Client Component, and Server Function are separate concepts with separate directives (or no directive). Confusing them produces code that fails to compile, fails at runtime, or exposes an unintended endpoint.
Remember: The "use server" directive does not mean “this code runs on the server.” It means “this function can be called from the client.” Server Components run on the server without any directive at all. The only directive a Server Component file should contain is none. When you see "use server", it is attached to a mutation function or a file of mutation functions, not to a component. When you see "use client", it is attached to a file that needs state, effects, or event handlers. When you see neither, the code is a Server Component, and that is exactly what it should be.
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!