React Support
React components and server components
React Support
Packem builds React component libraries, including Server Components, Client Components, and server actions. It understands the "use client" and "use server" directives and scopes the client/server boundaries correctly so the consuming app's bundler (such as Next.js) can transform them as intended.
Quick setup
The fastest way to enable React is the add command:
npx packem add reactThis will:
- Add
preset: 'react'to your config. - Install
react,react-dom, and the Babel dependencies. - Detect TypeScript and add
@types/reactand@types/react-domif TypeScript is installed (or prompt you to add TypeScript if it is not).
Manual configuration
The React preset configures Babel for JSX transformation:
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
export default defineConfig({
preset: 'react',
transformer,
})For more control you can configure Babel directly through rollup.babel:
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
export default defineConfig({
transformer,
externals: ['react', 'react-dom'],
rollup: {
babel: {
presets: [
['@babel/preset-react', { runtime: 'automatic' }],
],
},
},
})When Babel is used with React, it runs before your main transformer (esbuild/swc/etc.), turning JSX into JavaScript. TypeScript syntax is parsed by the transformer's parser plugins, not transformed by Babel. See Plugins for more on the Babel pipeline, including parallel transforms.
Server and Client Components
Packem supports building server components and server actions with the library directives "use client" and "use server". It generates the corresponding chunks for client and server, scoping the boundaries properly. When the library is integrated into an app such as Next.js, the app bundler can transform the client components and server actions correctly.
How directives are handled:
- If
"use client"or"use server"appears in an entry file, it is preserved at the top and that entry's output file becomes a client component. - If
"use client"or"use server"appears in a file that is used as a dependency of an entry, that file is split into a separate chunk and the directive is hoisted to the top of that chunk.
// src/button.tsx
'use client'
import { useState } from 'react'
export function Button() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}The directive on the entry above is preserved in the output, marking dist/button.js as a client component.
Shared modules across runtimes
When you build multiple runtime bundles (for example default and react-server together), some modules must remain a single shared instance across both. Use the shared module convention [name].shared-runtime.[ext] so the module is bundled into one chunk shared by every runtime bundle:
'use client'
// src/app-context.shared-runtime.js
export const AppContext = React.createContext(null)// src/index.js
import { AppContext } from './app-context.shared-runtime'// src/index.react-server.js
import { AppContext } from './app-context.shared-runtime'app-context.shared-runtime is bundled into a separate chunk with only one instance, shared among the different runtime bundles. See Multi-Runtime for how the react-server and other runtime entries are wired up.
CSS in React libraries
Packem processes CSS Modules and preprocessors imported by your components. See the CSS Processing guide and the React with CSS Modules example.
Next steps
- Multi-Runtime — react-server, edge-light, and other runtime bundles.
- Plugins — the Babel pipeline and presets.
- Server Components example — a full setup.