How do ya'll handle 'use client' with Turborepo shared UI libary?
Answered
Grass carp posted this in #help-forum
Grass carpOP
Shared UI library built with tsup.
- The interactive components have
In my
You can see the
Since everything bundled together, is it bad that I have
Cause at this point no matter which component I use from the shared UI library, I need to wrap it in
- The interactive components have
use client.In my
ui/dist/index.js, I get the error:The "use client" directive must be placed before other expressions. Move it to the top of the file to resolve this issue.You can see the
use client at the top of ui/dist/index.js"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }"use client"Since everything bundled together, is it bad that I have
use client in my ui library?Cause at this point no matter which component I use from the shared UI library, I need to wrap it in
use client in the consuming app even if there is nothing interactive.Answered by Grass carp
Hey, really appreciate your help!
So I ended up getting everything working. Although its unergonomic at this point. I may need to actually manually list my Components in
Doesn't seem you can fitler out recursive glob patterns.
How I import something now
So I ended up getting everything working. Although its unergonomic at this point. I may need to actually manually list my Components in
tsup.config.ts to get the import structure I want.Doesn't seem you can fitler out recursive glob patterns.
How I import something now
import { Button } from 'ui/Button/Button';41 Replies
Tsup is a bundler, it bundles all the components into a file. When you import a button from your ui library, other components like Dialog, Tabs etc. will also be imported and increase your bundle size.
I recommend exporting components independently, like
ui/button, ui/tabs etc, so that you can only ship required JavaScript to the clientConfigure the entry options in your tsup config
@Grass carp Shared UI library built with tsup.
- The interactive components have `use client`.
In my `ui/dist/index.js`, I get the error:
plaintext
The "use client" directive must be placed before other expressions. Move it to the top of the file to resolve this issue.
You can see the `use client` at the top of `ui/dist/index.js`
js
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }"use client"
Since everything bundled together, is it bad that I have `use client` in my ui library?
Cause at this point no matter which component I use from the shared UI library, I need to wrap it in `use client` in the consuming app even if there is nothing interactive.
since you are using a monorepo already, why not just eliminate the build step of that
ui package and use [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages)?[this monorepo](https://github.com/joulev/webapps) of mine uses typescript in the utility package and i don't need any build steps for it.
transpilePackages then it will just work. forget about tsup and everythingMaybe he is also publishing the library as a package, then it will be necessary to use a bundler like tsup
Grass carpOP
I really want to consume my ui library like its 3rd party
yeah if they are publishing a library to be used outside that monorepo, then building is necessary and pretend that my message never existed
but, in a monorepo, you don't need to build
if you don't intend to use that package elsewhere or publish it to npm
you don't need a build step
@joulev yeah if they are publishing a library to be used outside that monorepo, then building is necessary and pretend that my message never existed
Grass carpOP
I see, I appreciate the solution if I go that route.
and the build step would just be additional manpower and effort for nothing
Grass carpOP
Even if I don't publish, I still like the idea of ui package being treated as standalone.
@fuma I recommend exporting components independently, like `ui/button`, `ui/tabs` etc, so that you can only ship required JavaScript to the client
Grass carpOP
My current config in tsup
So for entry it would be
import { defineConfig, Options } from 'tsup';
export default defineConfig((options: Options) => ({
banner: {
js: `"use client"`,
},
// treeshake: true,
splitting: true,
entry: ['./src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
minify: true,
clean: true,
external: ['react'],
...options,
}));So for entry it would be
./src/components/atoms/Button, ./src/components/atoms/Accordion, every other component...You can use regex
“src/components/*.tsx†is supported
Btw remove the
banner option if you want to add “use client†manually. Tsup will add the directive if it’s at the top of entry file.Grass carpOP
what I'm getting now
Hm... I'm not getting an
index.d.ts at rootentry: [
'./src/components/*.tsx',
'./src/hooks/*.{ts,tsx}',
'./src/types/*.ts',
],Are you components in “src/components�
@Grass carp Hm... I'm not getting an `index.d.ts` at root
It is normal, because you don’t have an index file anymore
Grass carpOP
@fuma It is normal, because you don’t have an index file anymore
Grass carpOP
ah yeah, but then the consuming package gives the dreaded.
Cannot find module 'ui' or its corresponding type declarations.'./src/components/*/*.tsx'I don't want to export nested
components folder.So you will have to create a tsx file under components
And you need to add some extra configurations to
Mainly
https://github.com/SonMooSans/next-docs/blob/main/packages/next-docs-ui/package.json
package.json in order to make typescript workMainly
typesVersions and exports: https://github.com/SonMooSans/next-docs/blob/main/packages/next-docs-ui/package.json
Grass carpOP
nice link
this is going to take me some time...
Yeah… tbh refactoring codebase is a boring job xD
Grass carpOP
How do I exlude the nested components folder within a component (in my case
I have this
Accordion)I have this
'./src/components/**/*.tsx',, but it includes everythingI just want the top level
.tsxJust “./src/components/*.tsx�
Or
“./src/components/*/*.tsxâ€Grass carpOP
Hey, really appreciate your help!
So I ended up getting everything working. Although its unergonomic at this point. I may need to actually manually list my Components in
Doesn't seem you can fitler out recursive glob patterns.
How I import something now
So I ended up getting everything working. Although its unergonomic at this point. I may need to actually manually list my Components in
tsup.config.ts to get the import structure I want.Doesn't seem you can fitler out recursive glob patterns.
How I import something now
import { Button } from 'ui/Button/Button';Answer
Grass carpOP
I really do miss this structure
import {
Button,
Accordion,
Tabs,
} from 'ui';Well you can still do that tbf, there’s nothing wrong with client components; the pages router and pre-2023 react are all client components
@joulev Well you can still do that tbf, there’s nothing wrong with client components; the pages router and pre-2023 react are all client components
Grass carpOP
I just really want to adopt the philosophy of client components always on leaf nodes and not unnecessarily making something client when it doesn’t have to be. It is a lot of extra work with wrapping and unergonomic imports though.
Can’t have it all 🤷ðŸ»â€â™‚ï¸
Can’t have it all 🤷ðŸ»â€â™‚ï¸
Yeah. You have to choose one and discard one, can have them both at the same time
Grass carpOP
this is resolved