| 1 | --- |
| 2 | title: Avoid Shared Module State for Request Data |
| 3 | impact: HIGH |
| 4 | impactDescription: prevents concurrency bugs and request data leaks |
| 5 | tags: server, rsc, ssr, concurrency, security, state |
| 6 | --- |
| 7 | |
| 8 | ## Avoid Shared Module State for Request Data |
| 9 | |
| 10 | For React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race conditions, cross-request contamination, and security bugs where one user's data appears in another user's response. |
| 11 | |
| 12 | Treat module scope on the server as process-wide shared memory, not request-local state. |
| 13 | |
| 14 | **Incorrect (request data leaks across concurrent renders):** |
| 15 | |
| 16 | ```tsx |
| 17 | let currentUser: User |
| 18 | |
| 19 | export default async function |
| 20 | currentUser = await |
| 21 | return <Dashboard |
| 22 | } |
| 23 | |
| 24 | async function Dashboard() |
| 25 | return <div>{ |
| 26 | } |
| 27 | ``` |
| 28 | |
| 29 | If two requests overlap, request A can set `currentUser`, then request B overwrites it before request A finishes rendering `Dashboard`. |
| 30 | |
| 31 | **Correct (keep request data local to the render tree):** |
| 32 | |
| 33 | ```tsx |
| 34 | export default async function |
| 35 | const user = |
| 36 | return <Dashboard |
| 37 | } |
| 38 | |
| 39 | function Dashboard({ user |
| 40 | return <div>{ |
| 41 | } |
| 42 | ``` |
| 43 | |
| 44 | Safe exceptions: |
| 45 | |
| 46 | - Immutable static assets or config loaded once at module scope |
| 47 | - Shared caches intentionally designed for cross-request reuse and keyed correctly |
| 48 | - Process-wide singletons that do not store request- or user-specific mutable data |
| 49 | |
| 50 | For static assets and config, see [Hoist Static I/O to Module Level](./server-hoist-static-io.md |