react-native-web with Vite
Table of Contents
react-native-web
itself doesn’t require any tricky configuration to work with Vite.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
alias: {
"react-native": "react-native-web",
},
});
But in order to use any library for react-native or react-native-web this would be not enough.
First of all react-native
packages use convention to put web specific code in .web.js
files:
const extensions = [
".web.tsx",
".tsx",
".web.ts",
".ts",
".web.jsx",
".jsx",
".web.js",
".js",
".css",
".json",
".mjs",
];
export default defineConfig({
resolve: {
extensions,
}
optimizeDeps: {
esbuildOptions: {
resolveExtensions: extensions,
}
}
})
Often react-native
packages assume webpack and global values defined by it:
const development = process.env.NODE_ENV === "development";
export default defineConfig({
define: {
global: "window",
__DEV__: JSON.stringify(development),
DEV: JSON.stringify(development),
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
},
});
One more unpleasant surprise is that react-native
packages can distribute jsx
files with .js
extension:
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
loader: { ".js": "jsx" },
},
},
});
And if you see ReferenceError: React is not defined
cause by react-native
package, try:
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
jsx: "automatic",
},
},
});
Other issues which can happen (but I don’t have examples for it): js
files may contain Flow
syntax:
import { esbuildFlowPlugin } from "@bunchtogether/vite-plugin-flow";
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
plugins: [
esbuildFlowPlugin(/\.(flow|jsx?)$/, (path) =>
/\.jsx$/.test(path) ? "jsx" : "jsx"
),
],
},
},
});
Final config #
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// import { esbuildFlowPlugin } from "@bunchtogether/vite-plugin-flow";
// https://tamagui.dev/docs/intro/installation
const extensions = [
".web.tsx",
".tsx",
".web.ts",
".ts",
".web.jsx",
".jsx",
".web.js",
".js",
".css",
".json",
".mjs",
];
const development = process.env.NODE_ENV === "development";
// https://vitejs.dev/config/
export default defineConfig({
clearScreen: true,
plugins: [react()],
define: {
// https://github.com/bevacqua/dragula/issues/602#issuecomment-1296313369
global: "window",
__DEV__: JSON.stringify(development),
// https://tamagui.dev/docs/intro/installation
DEV: JSON.stringify(development),
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
},
resolve: {
extensions: extensions,
alias: {
"react-native": "react-native-web",
},
},
optimizeDeps: {
esbuildOptions: {
resolveExtensions: extensions,
// https://github.com/vitejs/vite-plugin-react/issues/192#issuecomment-1627384670
jsx: "automatic",
// need either this or the plugin below
loader: { ".js": "jsx" },
// plugins: [
// esbuildFlowPlugin(/\.(flow|jsx?)$/, (path) =>
// /\.jsx$/.test(path) ? "jsx" : "jsx"
// ),
// ],
},
},
});
Packages issues #
- https://github.com/jsartisan/react-native-magnus/issues/185
- https://github.com/wix/react-native-ui-lib/issues/2730
- https://github.com/software-mansion/react-native-reanimated/discussions/5007
Example #
Vite + react-native-web + TypeScript
Related #
vxrn #
vxrn
is a package that lets you serve your React Native apps using Vite. This is pretty cool as Vite typically doesn’t seem like it would “play well” with React Native - React Native only supports CommonJS, even for hot reloading, whereas Vite is all-in on ESModules.
vxrn
is in early alpha.
vite-plugin-react-native-web #
vite-plugin-react-native-web adds React Native Web support to Vite by removing Flow types, aliasing react-native to react-native-web and transforming .js files as .jsx files using ESBuild.
Read more: Component libraries trends, Styling components