Babel Plugin for Next.js 'use client' Directive
The `babel-plugin-transform-next-use-client` is a specialized Babel plugin designed to automatically inject the "use client" directive into React components within a Next.js application. It identifies components that utilize React client-only APIs, such as `useEffect` and `useState`, and programmatically adds the directive, ensuring these components are correctly designated for client-side rendering according to Next.js App Router conventions. This automation helps prevent common errors where client-specific code might accidentally be rendered on the server. The current stable version is 1.1.1. As a utility within the build toolchain, its release cadence is generally stable, with updates typically driven by significant changes in React or Next.js's component model. Its key differentiator is simplifying the management of client components, reducing manual boilerplate and ensuring proper separation of client and server code within the App Router paradigm. It also provides an option (`customClientImports`) to specify custom client-only hooks or modules for accurate detection.
Common errors
-
ReferenceError: window is not defined
cause A component intended to be client-side was inadvertently rendered on the server due to the 'use client' directive being missing, potentially because the plugin failed to detect a custom client API.fixVerify your `babel.config.js` includes the plugin. If you're using custom client-only hooks or modules, ensure they are listed in the `customClientImports` option of the plugin configuration. -
Error: Plugin 'babel-plugin-transform-next-use-client' not found.
cause The plugin is referenced in the Babel configuration but is not installed or incorrectly named.fixRun `npm install babel-plugin-transform-next-use-client` or `yarn add babel-plugin-transform-next-use-client`. Double-check the spelling in your `babel.config.js`.
Warnings
- breaking This plugin is specifically designed for Next.js applications utilizing the App Router and its client/server component architecture. It has no functional purpose or effect in Pages Router applications or non-Next.js React projects.
- gotcha The plugin relies on detecting common React client-only APIs (e.g., `useState`, `useEffect`). If you abstract these APIs into custom hooks or utility functions within separate modules, the plugin might not automatically add the 'use client' directive to the consuming component. This can lead to server-side rendering errors if those custom modules are implicitly client-only.
- gotcha Babel plugin order matters. Ensure this plugin runs *before* other transformations that might alter the AST in a way that prevents the detection of client-only APIs, or that might themselves introduce directives.
Install
-
npm install babel-plugin-transform-next-use-client -
yarn add babel-plugin-transform-next-use-client -
pnpm add babel-plugin-transform-next-use-client
Imports
- Babel Plugin String Name
import { transformNextUseClient } from 'babel-plugin-transform-next-use-client'plugins: ['babel-plugin-transform-next-use-client']
- Plugin with Options
plugins: ['babel-plugin-transform-next-use-client', { customClientImports: ['useMyClientHook'] }]plugins: [['babel-plugin-transform-next-use-client', { customClientImports: ['useMyClientHook'] }]]
Quickstart
{
// In your babel.config.js or .babelrc file
"presets": [
// ... other presets like '@babel/preset-env', '@babel/preset-react', 'next/babel'
],
"plugins": [
// ... other plugins
'babel-plugin-transform-next-use-client',
// If you have custom client-only hooks or modules, specify them:
// [
// 'babel-plugin-transform-next-use-client',
// {
// customClientImports: [
// 'useAuthClientStore', // Example: a custom hook that uses browser APIs
// 'myClientSideUtility'
// ]
// }
// ]
]
}
// Example React component that would trigger the plugin:
// components/MyClientComponent.tsx
// (No need for manual 'use client' here if plugin is active)
import React, { useState, useEffect } from 'react';
interface MyClientComponentProps {
initialCount?: number;
}
export default function MyClientComponent({ initialCount = 0 }: MyClientComponentProps) {
const [count, setCount] = useState(initialCount);
useEffect(() => {
// This effect runs only on the client
console.log('Component mounted on client');
document.title = `Count: ${count}`;
return () => {
console.log('Component unmounted from client');
};
}, [count]);
return (
<div>
<h1>Client Component Example</h1>
<p>Current count: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
<p>This component uses `useState` and `useEffect`, triggering the 'use client' directive automatically.</p>
</div>
);
}