Error using vite with RSD: stylex.create'should never be called at runtime.
Describe the issue
I created a react-ts app using vite cli to react-ts project, then add RSD library and follow setup steps.
When trying to render anything using RSD running vite command to run Vite Application this errors happens:
Uncaught Error: 'stylex.create' should never be called at runtime. It should be compiled away by '@stylexjs/babel-plugin'
Below this thread have more about discussion of this problem, possible Vite is not processing correctly RSD at runtime.
Expected behavior
Works a hello world project without any errors.
Steps to reproduce
1 - create react-ts app using vite: npm create vite@latest my-react-app -- --template react-ts
2 - add RSD into project
3 - make setup following the docs.
4 - try to render simple any DOM using RSD
5 - run yarn dev to run project
Test case
https://codesandbox.io/p/github/carloslibardo/website/main
Additional comments
Already tried to add "node_modules/react-strict-dom/dist/dom/runtime.js" into postcss.config.js and does not work, if i comment runtime.js stylex code it works, but still cannot apply my styles using stylex at my components.
https://codesandbox.io/p/github/carloslibardo/website/main
"Unable to access this workspace"
I recently got a Vite project running with Vite 6 and all latest versions 🙂
-
Run
yarn create vite, installreact-strict-domandpostcss-react-strict-dom -
Change
vite.config.ts
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import babel from 'vite-plugin-babel';
// https://vite.dev/config/
export default defineConfig({
plugins: [
react({
babel: {
presets: ['react-strict-dom/babel-preset']
}
}),
babel({
include: /node_modules\/react-strict-dom(-svg)?/
})
]
});
- Create
babel.config.cjs(only.cjsworks with Node.js 20)
module.exports = {
presets: [
['@babel/preset-react', { runtime: 'automatic' }],
'react-strict-dom/babel-preset'
]
};
- Create
postcss.config.js
export default {
plugins: {
'postcss-react-strict-dom': {
include: ['src/**/*.{js,jsx,ts,tsx}']
}
}
};
- Add
@stylex;at the top of yourindex.cssfile
Is the cjs limitation because we aren't providing mjs exports from the packages?
@necolas This is possibly the problem because Vite (like all bundlers) has many idiosyncrasies. However, Node has always been able to import CJS from ESM so CJS is usually considered the safer thing to publish to NPM.
We did run into similar issues with Vite before which is why we ended up publishing .mjs exports for some of the packages in StyleX.
Correction: postcss.config.js is also working without any problem. When working with Node < 22, you have to use babel.config.cjs to get vite-plugin-babel working correctly, but I found out that .js with ESM is also possible with NODE_OPTIONS=--experimental-require-module, or when using Node.js 22
I'm having the same issue as @carloslibardo I created #273 thinking it was a different issue but I think it is the same issue. I tried efoken configs but with no luck. I can also reproduce the issue using metro bundler. I am using pnpm and node 22. Here is a repository where you can see the issue with tsup & metro: https://github.com/fredericrous/rds-metro Here is a repository where you can see the issue with vite: https://github.com/fredericrous/rds-vite
@necolas could you explain the thumbs down on my message? Did you try one of these repository? Have you validated on your side efoken configuration? if this repo provided an example on how to create a component lib, export it and use it, it would be appreciated
I was on my phone and just mis-tapped the reaction icon
@fredericrous its not the same issue. The issue is that you try to run your web bundle of the ui package on native. In both of your repos, with Vite and also with Metro. Using PostCSS and a CSS file is for web.
Also in your storybook package I don't see the babel-preset and RSD setup, so you probably have not read/understood the documentation correctly or maybe never built a React Native ui library before.
When you want to run your ui library on native there is no need to build it before. Especially not with Web bundlers
thank you for the explanation @efoken
Indeed the storybook package has no babel-preset configured because I was expecting the library to do the transformation. You're right about my inexperience with building a UI library for native. There is very little documentation on this topic, but thanks to your valuable insights, I should be able to move forward.
I believe the value of react-strict-dom is not only to build like web on native but to enable sharing the same components across projects (both web and native). The documentation explains well how to use RDS directly on native or on web (and I successfully did use RDS on native). However, there seems to be a lack of documentation regarding how to create a reusable UI library. An example might be more effective than just documentation in clarifying this process.
That said, this discussion is slightly off-topic from the issue at hand. I hope the Vite-related problem described here finds a resolution
@necolas @efoken
bit of a long shot because I am still not able to replicate in a simpler setup... but maybe you have some ideas or encountered similar issues.
With a config similar to https://github.com/facebook/react-strict-dom/issues/265#issuecomment-2660129604 I got everything to work with a few interesting changes.
// babel.config.js
/* global process */
function getPlatform(caller) {
return caller && caller.platform;
}
function getIsDev(caller) {
return !!caller?.isDev;
}
export default function (api) {
const platform = api.caller(getPlatform) ?? "native";
console.log(`Babel config for platform: ${platform}`);
const dev = api.caller(getIsDev);
const isTest = process.env.NODE_ENV === "test";
api.cache.using(() => process.env.NODE_ENV === "development");
const plugins = ["module:react-native-reanimated/plugin"];
if (!isTest) {
plugins.push([
"@stylexjs/babel-plugin",
{
debug: dev,
dev: dev,
importSources: [{ from: "react-strict-dom", as: "css" }],
runtimeInjection: false,
styleResolution: "property-specificity",
unstable_moduleResolution: {
rootDir: process.cwd(),
type: "commonJS",
},
},
]);
}
return {
plugins,
presets: [
["module:@react-native/babel-preset"],
[
"module:react-strict-dom/babel-preset",
{
platform,
debug: dev,
dev,
},
],
],
};
}
Here I had to add the seemingly redundant @stylexjs/babel-plugin that in theory should be added with the exact same settings by the preset. No clue why...
// vite.config.js
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import babel from "vite-plugin-babel";
export default defineConfig(({ mode }) => ({
plugins: [
react({
babel: {
presets: [
[
"react-strict-dom/babel-preset",
{
debug: mode === "development",
dev: mode === "development",
platform: "web",
},
],
],
},
}),
babel({
include: /node_modules\/react-strict-dom/,
}),
],
}));
// postcss.config.js
import path from "node:path";
import { globSync } from "node:fs";
function getIncludePath(packageName) {
const url = new URL(import.meta.resolve(packageName));
return [
path.dirname(url.pathname) + "/**/*.{js,jsx,mjs,ts,tsx}",
"!" + path.dirname(url.pathname) + "/node_modules/**/*.{js,jsx,mjs,ts,tsx}",
];
}
function getSources() {
const paths = globSync("../**/*.{ts,tsx}", {
cwd: import.meta.dirname,
withFileTypes: true,
exclude: (dirent) => dirent.parentPath.includes("node_modules"),
});
return paths.map((file) => path.resolve(file.parentPath, file.name));
}
export default {
plugins: {
"react-strict-dom/postcss-plugin": {
include: [
...getSources(),
...getIncludePath("@my-org/tokens.stylex"),
...getIncludePath("react-strict-dom"),
],
useCSSLayers: true,
},
},
};
Here the complex getSources is needed to overcame the issue with pnpm using symlinks
Now, when I updated to newer versions of react-strict-dom that internally updated stylex to 0.13.1 first and then 0.14.1, this fragile setup stopped working and started showing Only static values are allowed inside of a createTheme() call error.
I did some digging and what I discovered is that code like
const baseTokens = {
colorBackgroundDefault: { default: "#ffffff", [dark]: "#000000" },
colorBackgroundPrimary: { default: "#000000", [dark]: "#ffffff" },
colorBackgroundSecondary: { default: "#66676e", [dark]: "#b6b8bc" },
gets transformed at some unknown point in the pipeline to calls to _defineProperty (babel very old polyfill for [dark]: ... syntax) and the stylex visitor is not able to recognize that form as a static value.
I have no clue why babel is doing that, and I don't see any _defineProperty in the generated files on the vite dev server, so I think it must be happening in an intermediate step.
So my questions are:
- do you have any idea about this
_definePropertystep looking at the config? Or maybe encountered same issue in the past? - do you have any clue why that redundant plugin is needed?
I know is a long shot, but hopefully my use case can also help identifying some additional sources of possible issues and gradually get to a point where we can have a reliable working setup to document for both storybook (native + web) as well as usage with pnpm
I'm going to answer myself, at least on the _defineProperty issue.
It seems that the problem is sharing the react-native babel-preset with the web vite config as well.
One alternative could be to pass babelrc: false and manually recreate a typescript config for vite, but if we want to reuse the same profile used by react-native, then we need to explicitly pass
presets: [
["module:@react-native/babel-preset",
{
unstable_transformProfile: "hermes-stable",
}
],
without the unstable_transformProfile, the preset by default assumes the hermes architecture is not used and applies the very old @babel/plugin-transform-computed-properties transform that generates those _defineProperty that makes the stylex compiler fail to recognize the static value
Hope this can be useful
I guess my config could help to start a doc for vite https://github.com/facebook/react-strict-dom/discussions/430#discussioncomment-15146095
I'm joining @MoOx’s comment. I think that with our two configuration examples #430 (comment) , we now have a solid basis for setting up with Vite (and TanStack Start).
Nice work. Would be great to have a PR to add a Vite setup in the repo's app examples