You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
'use client' directive for Client Components #4699
Next.js recently released v13 with a beta version of Client Components based on React's RFC 227. They recommend that third-party npm packages use the "use client" directive to indicate when a component is a Client Component, to avoid compilation issues.
Currently, builds using Rollup fail with "Module level directives cause errors when bundled" due to not supporting directives on a module.
Feature Proposal
Allow a 'use client' module-level directive to be preserved in bundling of a library.
The reason we forbid module level directives is because after bundling, they will end up in a merged chunk with lots of other modules without any module boundaries. So the directive would apply to all other modules as well, which is usually very problematic. If you need such directives in the output, I would recommend to add them via the banner option/plugin hook.
Reacted by Chase Adams, Lehoczky Zoltán, Ben McLean and Jakub Zomerfeld
Reacted by Michael Irigoyen, Filipe Degrazia, Max Prilutskiy, Chet Raj Gautam, Marcelo Fortunato, Andrew Fisher, Omar Habash and XGHeaven
You can ignore this warning/filter it via an onwarn handler. As this directive actually seems to have some meaning, I would recommend someone write a plugin for it that ensure it is handled correctly.
The reason we forbid module level directives is because after bundling, they will end up in a merged chunk with lots of other modules without any module boundaries. So the directive would apply to all other modules as well, which is usually very problematic. If you need such directives in the output, I would recommend to add them via the banner option/plugin hook.
Note if you use the banner option AND terser, terser will strip away your "use client" directive. Currently, one workaround is to disable the compress.directives option, but this is sub-optimal because it could result in duplicate directives. I've started an issue with the terser folks for a proper solution.
The other alternative is to use a banner plugin like rollup-plugin-banner2. Do NOT use rollup-plugin-banner, because it forces the banner into a comment. Make sure your banner plugin comes AFTER terser.
For a bundled build, directives like 'use client' is indeed all or nothing which is unfortunate. Is there anything stopping Rollup from preserving these directives when preserveModules: true though?
If that were the case, as library authors we could say "if you want to use the RSC features, you need to use this output format of the library", while still providing a bundled build for backwards compatibility.
Being unfamiliar with the code base and being limited on time I ran into two problems though:
I could not seem to find how to read the preserveModules option from there. Looks like you could access it from this.context.options.preserveModules, but that seems as if it's always undefined even if set to true in the config.
If I hardcoded a if (this.directive === "use client") return true in shouldBeIncluded (just to test), that directive is included below the imports which wont work.
Just thought I'd document my findings if someone else wants to take a stab at this (if such a contribution is even desirable?).
that directive is included below the imports which won't work
Yes, imports are removed and completely rewritten in the finalizers. To preserve directives, one would need to pass the directive to the finalizer, which is a bigger refactoring of the Rollup code base.
Yes, imports are removed and completely rewritten in the finalizers. To preserve directives, one would need to pass the directive to the finalizer, which is a bigger refactoring of the Rollup code base.
Thanks, that clarified it!
I gave a shot at implementing this as a plugin instead. It's a somewhat rough v0.1 (but feature complete, I think), there are no tests etc but in my own testing it seems to work well for us in the React Query build at least. Since it's my first Rollup plugin I would appreciate more eyes on it and help trying it out, and hope it helps someone else in this thread!
// rollup.config.jsimportpreserveDirectivesfrom"rollup-plugin-preserve-directives";exportdefault{output: {// If this is false, plugin does nothingpreserveModules: true,},plugins: [preserveDirectives()],};
Reacted by Mykhaylo Ryechkin, Lachlan Collins, James A Rosen, Stephen Goncalves, sho, Julien Cavaleiro, Clément Roche, Dominic Fournier, Sam Sze, Irvin Zhan and 14 more
Reacted by Ante Vukušić, Randhal, Mykhaylo Ryechkin, Sergei Lobanov, Rod Sot and Korat-Dishant
Is there any configuration needed in a bundler like the one nextjs uses to make it recognize and respect the "use client" directive in a component from an npm package?
I've created a component library, used Ephem's plugin to preserve the directives, and published it to a private npm registry . However, when I install it and try to use the compoentns directly in my nextjs pages they are being considered server components. If I wrap them in another component that is marked with "use client" they work but I don't want to have to do that.
We built a plugin rollup-preserve-directives for preservig directives like "use xxx" and used in a JS bundler, so far we experienced it with few libraries and it works pretty well. This plugin can be an optional solution if you need to keep the original directives in the bundle
Reacted by Will Stone, Aman Kumar, raxxo and Stephen Scholtz
The reason we forbid module level directives is because after bundling, they will end up in a merged chunk with lots of other modules without any module boundaries. So the directive would apply to all other modules as well, which is usually very problematic. If you need such directives in the output, I would recommend to add them via the banner option/plugin hook.
For those who don't understand what to do here, in your rollup config add this:
output: {
banner: "'use client';",
},
This adds 'use client'; to the top of every bundled .js file.
Reacted by Will Stone, German Quinteros, Andrew, Vrajpal Jhala, Alan Slater, DC, Alexander Ozhigin and Mario Volke
@Ben-at-Catalyst What about components you don't wish to add to the client-side bundle, and want them to benefit from server-side rendering? That's a great solution if every component in your library requires client-side JS to operate, but otherwise will unnecessarily bloat the JS bundle.
@will-stone I believe in that case you can add "use server" to the top of induvidual components instead of at the module level. My problem is I have an index.ts barrel file with "use client" at the top because i want the whole package to just be client rendered, this was breaking with rollup.
@Ben-at-Catalyst What about components you don't wish to add to the client-side bundle, and want them to benefit from server-side rendering? That's a great solution if every component in your library requires client-side JS to operate, but otherwise will unnecessarily bloat the JS bundle.
@Ben-at-Catalyst What about components you don't wish to add to the client-side bundle, and want them to benefit from server-side rendering? That's a great solution if every component in your library requires client-side JS to operate, but otherwise will unnecessarily bloat the JS bundle.
EDIT: Didn't realize it was actually discussed earlier in this thread, see this comment above.
I saw this plugin, but in my case I don't really care about the benefits of SSR and just need client components. I just thought with my original comment I'd try and explain the simplest action someone can take to resolve the error messages I was getting before finding this thread.
The reason we forbid module level directives is because after bundling, they will end up in a merged chunk with lots of other modules without any module boundaries. So the directive would apply to all other modules as well, which is usually very problematic. If you need such directives in the output, I would recommend to add them via the banner option/plugin hook.
Activity
lukastaegert commentedon Nov 2, 2022
The reason we forbid module level directives is because after bundling, they will end up in a merged chunk with lots of other modules without any module boundaries. So the directive would apply to all other modules as well, which is usually very problematic. If you need such directives in the output, I would recommend to add them via the
banneroption/plugin hook.lukastaegert commentedon Nov 2, 2022
Also, it is not an error but a warning https://rollupjs.org/repl/?version=3.2.5&shareable=JTdCJTIybW9kdWxlcyUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjJtYWluLmpzJTIyJTJDJTIyY29kZSUyMiUzQSUyMid1c2UlMjBjbGllbnQnJTNCJTIyJTJDJTIyaXNFbnRyeSUyMiUzQXRydWUlN0QlNUQlMkMlMjJvcHRpb25zJTIyJTNBJTdCJTIyZm9ybWF0JTIyJTNBJTIyZXMlMjIlMkMlMjJuYW1lJTIyJTNBJTIybXlCdW5kbGUlMjIlMkMlMjJhbWQlMjIlM0ElN0IlMjJpZCUyMiUzQSUyMiUyMiU3RCUyQyUyMmdsb2JhbHMlMjIlM0ElN0IlN0QlN0QlMkMlMjJleGFtcGxlJTIyJTNBbnVsbCU3RA==
You can ignore this warning/filter it via an
onwarnhandler. As this directive actually seems to have some meaning, I would recommend someone write a plugin for it that ensure it is handled correctly.0xcadams commentedon Nov 3, 2022
That makes sense, thanks for the quick response. Okay, we will look at configuration for preconstruct (which is using rollup under the hood).
jaslong commentedon Dec 20, 2022
Note if you use the banner option AND terser, terser will strip away your
"use client"directive. Currently, one workaround is to disable thecompress.directivesoption, but this is sub-optimal because it could result in duplicate directives. I've started an issue with the terser folks for a proper solution.The other alternative is to use a banner plugin like
rollup-plugin-banner2. Do NOT userollup-plugin-banner, because it forces the banner into a comment. Make sure your banner plugin comes AFTER terser.TkDodo commentedon Jan 4, 2023
Saw the warnings as well in our PR today:
use clientdirective at the top of files having client components TanStack/query#4738I'm questioning if we really need
"use client"to be present in a bundled version of the library? I think rollup ignoring it should be just fine ...zeilsell-user1 commentedon Jan 4, 2023
@jaslong, am I correct in saying that applying rollup-plugin-banner2 with 'use client' then the whole library would be tagged as client side?
mryechkin commentedon Mar 9, 2023
This unfortunately is also the case with the
compress.directivesworkaround that @jaslong mentioned. Is there even a way around that though?Ephem commentedon Mar 11, 2023
For a bundled build, directives like
'use client'is indeed all or nothing which is unfortunate. Is there anything stopping Rollup from preserving these directives whenpreserveModules: truethough?If that were the case, as library authors we could say "if you want to use the RSC features, you need to use this output format of the library", while still providing a bundled build for backwards compatibility.
Ephem commentedon Mar 11, 2023
I did some digging into this to see if it was something I could contribute on. I found where this is happening: https://github.com/rollup/rollup/blob/master/src/ast/nodes/ExpressionStatement.ts
Being unfamiliar with the code base and being limited on time I ran into two problems though:
preserveModulesoption from there. Looks like you could access it fromthis.context.options.preserveModules, but that seems as if it's alwaysundefinedeven if set to true in the config.if (this.directive === "use client") return trueinshouldBeIncluded(just to test), that directive is included below the imports which wont work.Just thought I'd document my findings if someone else wants to take a stab at this (if such a contribution is even desirable?).
lukastaegert commentedon Mar 11, 2023
Yes, imports are removed and completely rewritten in the finalizers. To preserve directives, one would need to pass the directive to the finalizer, which is a bigger refactoring of the Rollup code base.
Ephem commentedon Mar 12, 2023
Thanks, that clarified it!
I gave a shot at implementing this as a plugin instead. It's a somewhat rough v0.1 (but feature complete, I think), there are no tests etc but in my own testing it seems to work well for us in the React Query build at least. Since it's my first Rollup plugin I would appreciate more eyes on it and help trying it out, and hope it helps someone else in this thread!
https://github.com/Ephem/rollup-plugin-preserve-directives
Quick version:
npm install -D rollup-plugin-preserve-directivesmryechkin commentedon Mar 13, 2023
@Ephem you're a legend, thank you! Plugin works perfectly for me. Helped that I was already using
preserveModules: truein my config 😄Ephem commentedon Mar 14, 2023
@mryechkin Glad to hear its working well! If that changes, please let me know. 😉
23 remaining items
gmaghame commentedon Mar 7, 2024
Is there any configuration needed in a bundler like the one nextjs uses to make it recognize and respect the "use client" directive in a component from an npm package?
I've created a component library, used Ephem's plugin to preserve the directives, and published it to a private npm registry . However, when I install it and try to use the compoentns directly in my nextjs pages they are being considered server components. If I wrap them in another component that is marked with "use client" they work but I don't want to have to do that.
imtheaman commentedon Mar 15, 2024
@gmaghame Hey, please keep us updated if ya find any solution to the problem
huozhi commentedon Mar 15, 2024
We built a plugin rollup-preserve-directives for preservig directives like
"use xxx"and used in a JS bundler, so far we experienced it with few libraries and it works pretty well. This plugin can be an optional solution if you need to keep the original directives in the bundlehiWaldoDavid commentedon May 16, 2024
I ran into this issue yesterday and spent quite a bit of time searching for a solution. I'll post mine here for anyone else that gets stuck.
rollup.config.js, in the root directory of the library you want to build../node_modules/@nx/react/plugins/bundle-rollup.jsinto the new file.preserveDirectivesas the last entry in the plugins list (adding it before will throw an error).options.outputto includepreserveModules: true. Output can be an array, so it needs to be included for each build format.project.jsonfile, pointtargets.build.options.rollupConfigto the new config file.This is what I have:
I made a few minor changes, but kept it mostly as I found it in the plugin folder.
This is bundling my library with the
'use client'directives, and without any warnings.Ben-at-Catalyst commentedon Aug 7, 2024
For those who don't understand what to do here, in your rollup config add this:
This adds
'use client';to the top of every bundled.jsfile.will-stone commentedon Aug 7, 2024
@Ben-at-Catalyst What about components you don't wish to add to the client-side bundle, and want them to benefit from server-side rendering? That's a great solution if every component in your library requires client-side JS to operate, but otherwise will unnecessarily bloat the JS bundle.
Ben-at-Catalyst commentedon Aug 7, 2024
@will-stone I believe in that case you can add "use server" to the top of induvidual components instead of at the module level. My problem is I have an
index.tsbarrel file with "use client" at the top because i want the whole package to just be client rendered, this was breaking with rollup.mryechkin commentedon Aug 8, 2024
You could use the rollup-plugin-preserve-directives plugin to selectively add
"use client"only to the components that need it.I wrote about this on my blog here for reference: https://www.misha.wtf/blog/rollup-server-components
EDIT: Didn't realize it was actually discussed earlier in this thread, see this comment above.
Ben-at-Catalyst commentedon Aug 8, 2024
I saw this plugin, but in my case I don't really care about the benefits of SSR and just need client components. I just thought with my original comment I'd try and explain the simplest action someone can take to resolve the error messages I was getting before finding this thread.
Add use client directive at top of client-component files (#2468)
szhsin commentedon Feb 8, 2025
For anyone looking for a plugin to selectively add 'use client' based on glob pattern matching the module name, here's a simple one I created.
https://github.com/szhsin/rollup-plugin-add-directive
Hope it helps!
Fix qs-override regression (undid a real security fix) + use-client w…
Fix qs-override regression (undid a real security fix) + use-client w…
Fix qs-override regression (undid a real security fix) + use-client w…
Fix qs-override regression (undid a real security fix) + use-client w…