| 1 | + | --- |
| 2 | + | title: React |
| 3 | + | category: Integrations |
| 4 | + | order: 3 |
| 5 | + | --- |
| 6 | + | |
| 7 | + | import Link from "../../../../components/Link.jsx"; |
| 8 | + | |
| 9 | + | # React |
| 10 | + | |
| 11 | + | `@lazarv/react-server` ships with React built-in. You **do not need to install `react` or `react-dom`** yourself — the runtime includes the correct experimental version of React that supports React Server Components, server actions, and all the latest React features. |
| 12 | + | |
| 13 | + | <Link name="why-react-is-built-in"> |
| 14 | + | ## Why React is built-in |
| 15 | + | </Link> |
| 16 | + | |
| 17 | + | React Server Components require an experimental build of React that includes the RSC wire protocol, server references, and streaming support. These experimental APIs are not available in the stable releases published to npm. By bundling the exact React version it was built against, `@lazarv/react-server` guarantees: |
| 18 | + | |
| 19 | + | - **Correct RSC support** — the `react-server` export condition, `react-server-dom-webpack`, and streaming primitives all match. |
| 20 | + | - **Zero configuration** — you don't need to figure out which experimental React build to install, or keep it in sync across `react`, `react-dom`, and `react-server-dom-webpack`. |
| 21 | + | - **Single React instance** — the runtime ensures only one copy of React is loaded at any time, avoiding the notorious "multiple React instances" bugs (broken hooks, broken context, etc.). |
| 22 | + | |
| 23 | + | <Link name="no-react-in-your-package-json"> |
| 24 | + | ## No React in your package.json |
| 25 | + | </Link> |
| 26 | + | |
| 27 | + | When you create a project with `@lazarv/react-server`, your `package.json` should **not** list `react` or `react-dom` as dependencies: |
| 28 | + | |
| 29 | + | ```json filename="./package.json" |
| 30 | + | { |
| 31 | + | "dependencies": { |
| 32 | + | "@lazarv/react-server": "latest" |
| 33 | + | } |
| 34 | + | } |
| 35 | + | ``` |
| 36 | + | |
| 37 | + | That's it. The runtime provides React for your entire project — server components, client components, and SSR. |
| 38 | + | |
| 39 | + | If you are migrating from an existing React project, **remove `react` and `react-dom`** from your dependencies: |
| 40 | + | |
| 41 | + | ```sh |
| 42 | + | pnpm remove react react-dom |
| 43 | + | ``` |
| 44 | + | |
| 45 | + | <Link name="how-it-works"> |
| 46 | + | ## How it works |
| 47 | + | </Link> |
| 48 | + | |
| 49 | + | `@lazarv/react-server` lists `react`, `react-dom`, and `react-server-dom-webpack` as direct **dependencies** (not peer dependencies). At both development and build time, the runtime sets up module aliases so that any `import` of `react` or `react-dom` — whether from your code or from third-party libraries — resolves to the React version bundled with the runtime. |
| 50 | + | |
| 51 | + | This aliasing operates at the Vite module resolution level across all three environments: |
| 52 | + | |
| 53 | + | - **`rsc`** — React Server Components use the `react-server` export condition (`react.react-server.js`) |
| 54 | + | - **`ssr`** — server-side rendering uses the standard React entry (`index.js`) |
| 55 | + | - **`client`** — client components in the browser use the same React version, bundled by Vite |
| 56 | + | |
| 57 | + | Because the aliasing intercepts resolution before any bundling happens, third-party libraries that `import` from `react` or `react-dom` will transparently use the runtime's React — no configuration needed. |
| 58 | + | |
| 59 | + | <Link name="third-party-library-compatibility"> |
| 60 | + | ## Third-party library compatibility |
| 61 | + | </Link> |
| 62 | + | |
| 63 | + | Most React libraries work out of the box with `@lazarv/react-server`. The key compatibility considerations are: |
| 64 | + | |
| 65 | + | ### Libraries that work seamlessly |
| 66 | + | |
| 67 | + | Any library that depends on `react` or `react-dom` via standard imports will work, because module aliasing ensures it receives the runtime's React. This includes: |
| 68 | + | |
| 69 | + | - **UI component libraries** — [Material UI](/integrations/mui), [Mantine](/integrations/mantine), Chakra UI, Radix, shadcn/ui, Headless UI, etc. |
| 70 | + | - **State management** — Zustand, Jotai, Recoil, Redux (in client components) |
| 71 | + | - **Data fetching** — [TanStack Query](/integrations/react-query), SWR, Apollo Client (in client components) |
| 72 | + | - **Animation** — Framer Motion, React Spring |
| 73 | + | - **Forms** — React Hook Form, Formik (in client components) |
| 74 | + | - **Styling** — Emotion, Styled Components, Tailwind CSS, CSS Modules |
| 75 | + | |
| 76 | + | ### Client vs. server components |
| 77 | + | |
| 78 | + | Third-party libraries that use React hooks (`useState`, `useEffect`, `useRef`, etc.) or browser APIs must be used inside [client components](/guide/client-components) (files with `"use client"` directive). This is a React Server Components rule, not a `@lazarv/react-server` limitation. |
| 79 | + | |
| 80 | + | ```jsx filename="./src/Counter.jsx" |
| 81 | + | "use client"; |
| 82 | + | |
| 83 | + | import { useState } from "react"; |
| 84 | + | import { motion } from "framer-motion"; |
| 85 | + | |
| 86 | + | export default function Counter() { |
| 87 | + | const [count, setCount] = useState(0); |
| 88 | + | return ( |
| 89 | + | <motion.button |
| 90 | + | whileTap={{ scale: 0.95 }} |
| 91 | + | onClick={() => setCount(count + 1)} |
| 92 | + | > |
| 93 | + | count is {count} |
| 94 | + | </motion.button> |
| 95 | + | ); |
| 96 | + | } |
| 97 | + | ``` |
| 98 | + | |
| 99 | + | Server components can import and render client components, but cannot use hooks or browser APIs themselves. See the [client components guide](/guide/client-components) and [server components guide](/guide/server-components) for more details. |
| 100 | + | |
| 101 | + | ### Libraries with React version checks |
| 102 | + | |
| 103 | + | Some libraries perform runtime version checks (e.g., reading `React.version`). Since `@lazarv/react-server` uses an experimental React build, the version string looks like `0.0.0-experimental-...` rather than a stable `19.x.x`. Most libraries handle this gracefully, but in rare cases a library might warn or refuse to load because it doesn't recognize the version. |
| 104 | + | |
| 105 | + | If you encounter this, the library usually still works correctly — the warning can be safely ignored. If a library strictly blocks on version, check whether a newer version of that library has relaxed its version check, or open an issue with the library maintainer. |
| 106 | + | |
| 107 | + | ### Libraries that bundle their own React |
| 108 | + | |
| 109 | + | Occasionally, a library might bundle its own copy of React instead of importing it as a peer dependency. This can cause the "multiple React instances" problem — hooks errors (`Invalid hook call`) or context not being shared. However, `@lazarv/react-server` already deduplicates `react` and `react-dom` automatically, so most of these cases are handled for you without any extra configuration. |
| 110 | + | |
| 111 | + | <Link name="typescript-types"> |
| 112 | + | ## TypeScript types |
| 113 | + | </Link> |
| 114 | + | |
| 115 | + | For TypeScript users, you should use the experimental React types in your `tsconfig.json`: |
| 116 | + | |
| 117 | + | ```json filename="./tsconfig.json" |
| 118 | + | { |
| 119 | + | "compilerOptions": { |
| 120 | + | "types": ["react/experimental", "react-dom/experimental"] |
| 121 | + | } |
| 122 | + | } |
| 123 | + | ``` |
| 124 | + | |
| 125 | + | This ensures your editor recognizes the latest React APIs like `"use client"`, `"use server"`, `useActionState`, `useFormStatus`, and other experimental features. See the [TypeScript integration guide](/integrations/typescript) for a complete configuration example. |
| 126 | + | |
| 127 | + | <Link name="checking-the-react-version"> |
| 128 | + | ## Checking the React version |
| 129 | + | </Link> |
| 130 | + | |
| 131 | + | To see which React version `@lazarv/react-server` is using, you can check it at runtime: |
| 132 | + | |
| 133 | + | ```jsx |
| 134 | + | import { version } from "react"; |
| 135 | + | |
| 136 | + | export default function Version() { |
| 137 | + | return <p>React version: {version}</p>; |
| 138 | + | } |
| 139 | + | ``` |
| 140 | + | |
| 141 | + | Or inspect the installed version via the CLI: |
| 142 | + | |
| 143 | + | ```sh |
| 144 | + | node -e "console.log(require('@lazarv/react-server/node_modules/react/package.json').version)" |
| 145 | + | ``` |