vanilla-extract: errors when using external ui library implemented with vanilla-extract in next.js app
Describe the bug
In a Next.js app using the @vanilla-extract/next-plugin to support VE locally, everything works as expected. However, when trying to use view code from an external library that has styles implemented with vanilla-extract, there are render errors.
The minimal example for can be found here:
- Next.js app
- @jneander/x-throwaway-vanilla-extract-ui library (This is used by the app linked above and is linked here for reference.)
The following behavior is observed while the Next.js app is running in dev mode:
A. The external code is not used on the page.
- Refresh the page.
- Observe that the page loads successfully.
B. The external code is added to the page.
- Observe that the page injects the external code to the page successfully.
- Refresh the page.
- Observe that the page displays the error described below.
Server Error
Error: Styles were unable to be assigned to a file. This is generally caused by one of the following:
- You may have created styles outside of a '.css.ts' context
- You may have incorrect configuration. See https://vanilla-extract.style/documentation/getting-started
Reproduction
https://github.com/jneander/example-app-using-third-party-vanilla-extract-ui-library
System Info
System:
OS: macOS 13.0
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 359.04 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 18.12.1 - ~/.nvm/versions/node/v18.12.1/bin/node
Yarn: 1.22.17 - /usr/local/bin/yarn
npm: 8.19.2 - ~/.nvm/versions/node/v18.12.1/bin/npm
Browsers:
Chrome: 108.0.5359.94
Firefox: 97.0.2
Safari: 16.1
npmPackages:
@vanilla-extract/css: ^1.9.2 => 1.9.2
@vanilla-extract/next-plugin: ^2.1.1 => 2.1.1
Used Package Manager
npm
Logs
Server Error
Error: Styles were unable to be assigned to a file. This is generally caused by one of the following:
- You may have created styles outside of a '.css.ts' context
- You may have incorrect configuration. See https://vanilla-extract.style/documentation/getting-started
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Call Stack
Object.getFileScope
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/@vanilla-extract/css/fileScope/dist/vanilla-extract-css-fileScope.cjs.dev.js (33:11)
generateIdentifier
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js (183:49)
style
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js (404:19)
Object.<anonymous>
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/@jneander/x-throwaway-vanilla-extract-ui/dist/cjs/example.css.js (5:32)
Module._compile
node:internal/modules/cjs/loader (1159:14)
Module._extensions..js
node:internal/modules/cjs/loader (1213:10)
Module.load
node:internal/modules/cjs/loader (1037:32)
Module._load
node:internal/modules/cjs/loader (878:12)
Module.require
node:internal/modules/cjs/loader (1061:19)
require
node:internal/modules/cjs/helpers (103:18)
Object.<anonymous>
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/@jneander/x-throwaway-vanilla-extract-ui/dist/cjs/example.js (5:23)
Module._compile
node:internal/modules/cjs/loader (1159:14)
Module._extensions..js
node:internal/modules/cjs/loader (1213:10)
Module.load
node:internal/modules/cjs/loader (1037:32)
Module._load
node:internal/modules/cjs/loader (878:12)
Module.require
node:internal/modules/cjs/loader (1061:19)
require
node:internal/modules/cjs/helpers (103:18)
Object.<anonymous>
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/@jneander/x-throwaway-vanilla-extract-ui/dist/cjs/index.js (17:14)
Module._compile
node:internal/modules/cjs/loader (1159:14)
Module._extensions..js
node:internal/modules/cjs/loader (1213:10)
Module.load
node:internal/modules/cjs/loader (1037:32)
Module._load
node:internal/modules/cjs/loader (878:12)
Module.require
node:internal/modules/cjs/loader (1061:19)
require
node:internal/modules/cjs/helpers (103:18)
@jneander/x-throwaway-vanilla-extract-ui
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/pages/index.js (54:18)
__webpack_require__
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/webpack-runtime.js (33:42)
eval
webpack-internal:///./pages/index.tsx (8:98)
./pages/index.tsx
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/pages/index.js (43:1)
__webpack_require__
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/webpack-runtime.js (33:42)
__webpack_exec__
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/pages/index.js (75:39)
<unknown>
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/pages/index.js (76:28)
Object.<anonymous>
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/.next/server/pages/index.js (79:3)
Module._compile
node:internal/modules/cjs/loader (1159:14)
Module._extensions..js
node:internal/modules/cjs/loader (1213:10)
Module.load
node:internal/modules/cjs/loader (1037:32)
Module._load
node:internal/modules/cjs/loader (878:12)
Module.require
node:internal/modules/cjs/loader (1061:19)
require
node:internal/modules/cjs/helpers (103:18)
Object.requirePage
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/require.js (88:12)
<unknown>
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/load-components.js (37:73)
async Object.loadComponents
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/load-components.js (37:26)
async DevServer.findPageComponents
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/next-server.js (548:36)
async DevServer.renderPageComponent
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/base-server.js (926:24)
async DevServer.renderToResponse
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/base-server.js (955:32)
async DevServer.pipe
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/base-server.js (406:25)
async Object.fn
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/next-server.js (744:21)
async Router.execute
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/router.js (252:36)
async DevServer.run
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/base-server.js (383:29)
async DevServer.run
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/dev/next-dev-server.js (734:20)
async DevServer.handleRequest
file:///Users/jeremy/Projects/temp/example-app-with-ve-bug/node_modules/next/dist/server/base-server.js (321:20)
Validations
- Check that there isn’t already an issue that reports the same bug to avoid creating a duplicate.
- The provided reproduction is a minimal reproducible example of the bug.
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 3
- Comments: 27 (11 by maintainers)
I appreciate all of the ideas and collaboration here. ❤️ I’ll make my way back to this eventually. Work has been eating me alive and I don’t have anything left in the tank right now for tackling this issue.
I think I found a definitive fix. Make this change to your next.config.js:
This will make it so that the imports are consistent between client and server. You also need to write an exports field in your package as well as your main and module fields (which I thought was pretty weird but it consistently fixed the issue):
Finding this was a result of a very frustrating trial an error process, but I’m 100% sure that this is a problem on Next.js’ fault. If the original poster could try this I think we could also close the issue 😃
EDIT:
After we did some internal investigation, this solution was weirdly only working inside of our monorepo, not in packages consuming our toolkit. The definite absolutely final solution was actually to add our VE-dependant packages to the
transpilePackageskey on the next.js configuration. Next.js Docs@viclafouch we had some challenges with VE UI Library to integrate it with Vite. With some tweaking in the Vite config, we succeed to make it work. The hack was to exclude the UI library packages from the “optimizeDeps”.
Dep Optimization Options
This is how our vite.config.ts looks now:
Worth mentioning is that from “@filamix-ui” we are exporting subpaths thru package.json. It works fine. Just make sure in your tsconfig.json to set “compilerOptions” > “moduleResolution” to “Node16”.
Any new findings on this @jneander ? I have the same case as you, UI library that exports VE and I need to support both esm and cjs.
As I stated in a previous edit, I think this is a problem on Next.js’ part. Just by looking at the classNames generated, I can see that the server is serving from the cjs module while the client is importing from the esm module. There is a stale issue about this on their github (https://github.com/vercel/next.js/issues/35581). The workaround is deleting the “module” key from our package.json.
Sure! It indeed is possible, however as I said, you may end up running into scoping issues. We extensively use contracts for our values, and there is one main theme which gets consistently referenced by all our components. If at some point, someone decides to make a small change to the main theme, it means that only the theme package needs to be updated. This would not be the case with pre-built CSS, since building the theme package would generate a new hash for the variable scope and thus all the CSS that references that would have to be rebuilt, even if they weren’t changed at all. Since all of our contracts also come from the same place, it would mean that adding a new component would have to trigger a new version for all the components as well, which pretty much just invalidates the reasoning for having separate component versions.
It looks like this is more of a feature request than a bug. Shipping styles without compilation isn’t really how Vanilla Extract is meant to be used. As a UI library author, I don’t see the benefit in forcing every user to compile the VE styles themselves from node_modules.
My component library is working without issues in Next.js v13.1.2 app directory. For reference, here’s the library source code and usage in a Next.js app.
For anyone getting blocked by this, the solution might just be to add the respective bundler plugin to your library so you’re shipping static styles, not TypeScript. And if there are any significant benefits to shipping TypeScript and making users compile VE styles, I’d love to see a docs page on this.
I think I’ve been running into the same thing (with just the Webpack plugin). For me, it boiled down to the fact that my component library is published in CJS, and I don’t believe that the Vanilla Extract tooling supports CommonJS. I think I have a fix in #970.
In the meantime, I found that the workaround is to include the
@vanilla-extract/babel-plugin(now deprecated) in the library build step. It appears to inject theaddFileScopestuff prior to the file being converted to ESM. So now my publishedButton.css.jsfile looks something like this:Then when the app consumes, this file,
@vanilla-extract/integrationdoesn’t need to care if the file is CJS or ESM. See here.I don’t think this is the ideal solution by any means, but it’s a decent work around, and I think this can be handled upstream (maybe my PR is the right solution, maybe not).
Hi @jneander. Sorry for the delayed response.
In my original comment I thought you were trying to achieve something different, but given the context you’ve provided I think I now understand the problem better.
As far as I know, there’s no reason your approach shouldn’t work. Vanilla extract integrations should find files ending in
.css.tsOR.css.js. The example repo linked in the issue you referenced transpiles its files to javascript, and those files are consumed just fine in the app adjacent to the components library. https://github.com/mihkeleidast/vanilla-extract-component-library-exampleIn fact, my team at SEEK is working on a packaging tool that works by this principle, as our design system has gotten large enough that it has warranted investment in speeding up build times. Currently we just ship raw typescript and put the onus on the consumer to transpile it in their app, which as you’ve mentioned, doesn’t scale very well.
The error you mentioned in the top-level issue comment is coming from here, which is called slightly further down the call stack from the
stylefunction in yourcss.jsfile. The webpack plugin should be inserting asetFileScopecall beforestyleis called, which would prevent that error from being thrown.Debugging your app, I noticed that the VE webpack plugin was transforming the esm version of
example.css.js, and then the app was loading the untransformed cjs version ofexample.css.js, causing the error. Why this is happening I’m not so sure about, but that seems to be the crux of the issue. This could be something to do with the component library’spackage.json, maybe anexportskey could do the trick. I’m not super knowledgeable about ESM resolution, but I do know it has some weird quirks that can lead to unexpected results.Hope this helps.
Hi, thanks for the reproduction.
Your app looks fine to me. I think the issue might be how your UI library is bundled. You’ll need to use one of the bundler integrations to create a package that can be consumed correctly in a project that uses vanilla-extract.
Take a look at the webpack or the rollup integrations for some ideas of how to get that working. Alternatively, a discord user recently shared an example repo that uses rollup to bundle a UI package that uses vanilla-extract for styling. https://github.com/graup/vanilla-extract-rollup-example/tree/main/packages/ui.
Feel free to join the discord for some quicker feedback from community members if you need some help getting things working.