one
one copied to clipboard
Add `nativewind@4` support
It would be amazing if we could use nativewind with one, but a lot of things should be made to do so.
- In order to
nativewind@4work we need a vite plugin which transforms css toreact-native-css-interopstyles, essentially this:
// vite.config.ts
import type { Plugin } from "vite"
import { cssToReactNativeRuntime } from 'react-native-css-interop/css-to-rn'
function nativewind(): Plugin {
return {
name: "nativewind",
transform: (code, id) => {
if (id.endsWith(".css")) {
const res = cssToReactNativeRuntime(code)
return { code: `import { StyleSheet } from "nativewind"\nStyleSheet.registerCompiled(${JSON.stringify(res)})`, map: null }
}
return { code }
}
}
}
-
We also need to change
jsxImportSourcetonativewind, this seems kinda impossible without patching right now (I've tried to do aenforce: "post"plugin that replacesreact/jsx-runtimewithnativewind/jsx-runtimebut it doesn't work) -
And we also need a babel plugin
nativewind/babelwhich essentially replacescreateElementfromreactimports withcreateInteropElementfromreact-native-css-interop(this plugin is only necessary when the user explicit calls createElement instead of using JSX)
So instead of adding all these things, we can allow users to change the jsxImportSource and also allow then to add some sort of babel plugin that run on the entire codebase. The vite plugin could live on another repository.
What you think?
It’s definitely doable, Vite is if anything super configurable.
I am surprised at how much setup they have to make that work, wow. It almost seems easier to make Tamagui work with tailwind classNames in a funny way, especially since it has no setup cost at all. I’ve been considering doing this, a bit more as of late. Maybe it’s worth me trying to put together a prototype. The nice thing is it may actually be faster on web and native, and easier to set up. But no guarantee.
For getting nativewind to work, you’ll want to configure things using Vite 6 environments api for theios and android environment.
Separately, we probably could make adding babel and other things easier independently, and allowing this configuration for jsxImportSource, but it'd need to be done well so that it works well with Vite environment APIs. If you make progress on that feel free to push a PR and I'll help along.
I'm looking into this too, @ceopaludetto lmk if I can help :)
Any update? 👀
tamagui but expressed as tw would be fire
i'm redoing babel setup here:
- https://github.com/onejs/one/pull/374
once that lands i will revisit this as it should fix the babel side
Got it to here:
It seems to be transforming it right:
And you can see its getting to CSSInterop.View, but maybe its grabbing the web path? I tried to delete the web files just to see but still got this. Not sure.
react-native-css-interop was added to optmizeDeps? Maybe you need to set process.env.NATIVEWIND_OS to web or native, nativewind need this to correctly pick the preset to tailwindcss. I've created the following vite plugin to fix that:
{
name: "nativewind",
enforce: "pre",
buildStart() {
process.env.NATIVEWIND_OS = ["android", "ios"].includes(this.environment.name) ? "native" : "web"
}
}
See: https://github.com/nativewind/nativewind/blob/main/packages/nativewind/src/tailwind/index.ts
When you say that is incorrectly picking the web version, do you mean the jsxImportSource? Adding the className directly to the View works? Like this:
import { View, Text } from "react-native"
export default function Index() {
return (
<View className="bg-red-500 p-4">
<Text>Hi</Text>
</View>
)
}
If you're getting typescript errors, try this in a nativewind.d.ts file:
/// <reference types="nativewind/types" />
no className on View and <div /> both fail the same way:
I tried looking again this morning, it seems to be importing all the right nativewind/css-interop native files. Lots of console logs look right.
But what happens in the end is the CssInterop.View that it renders ends up with it's children prop being the original <div />. So it does render the right nativewind view, and I can see the source looks fine, I have to think that somehow nativewind is being run but not transforming the props it gets into the right final form, which feels like it's some sort of case where One is either not grabbing a .native file somewhere, or there's some like other detection inside nativewind that is going down the wrong path and thinks its on web for some reason.
Here's the PR:
- https://github.com/onejs/one/pull/408
You can run yarn dev:example tailwind and if you add // debug to the top of any file see the transforms, they look right.
Here's the PR:
* [feat(one): support nativewind with no configuration #408](https://github.com/onejs/one/pull/408)You can run
yarn dev:example tailwindand if you add// debugto the top of any file see the transforms, they look right.
I'm trying right now
After trying for a while, some notes:
jsxImportSourceshould be nativewind even on the web, since this should enable className support for rn components such as<View />and<Text />. I've made this work by changing a condition inpackages/compiler/src/transformSwc.ts:40to:
const enableNativeCSS = configuration.enableNativeCSS
nativewind/jsx-runtimefails on SSR due to being a cjs dependency, maybe it needs vite interop. I've got the following error and didn't manage to solve (even usingdepsconfiguration), so instead I've changed to spa mode just to continue testing
Stack
ReferenceError: module is not defined
at eval (/Users/carlos/Documents/Projetos/Javascript/one/node_modules/nativewind/jsx-dev-runtime/index.js:1:8)
at ESModulesEvaluator.runInlinedModule (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1057:6)
at ModuleRunner.directRequest (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1271:82)
at ModuleRunner.cachedRequest (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1166:28)
at request (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1215:79)
at async eval (/Users/carlos/Documents/Projetos/Javascript/one/examples/one-tailwind/app/index.tsx:3:44)
at async ESModulesEvaluator.runInlinedModule (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1049:5)
at async ModuleRunner.directRequest (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1271:61)
at async ModuleRunner.cachedRequest (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1167:76)
at async ModuleRunner.import (file:///Users/carlos/Documents/Projetos/Javascript/one/node_modules/vite/dist/node/module-runner.js:1104:12)
at async Object.handlePage (file:///Users/carlos/Documents/Projetos/Javascript/one/packages/one/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs:52:52)
at async file:///Users/carlos/Documents/Projetos/Javascript/one/packages/one/dist/esm/createHandleRequest.mjs:82:97
at async runMiddlewares (file:///Users/carlos/Documents/Projetos/Javascript/one/packages/one/dist/esm/createHandleRequest.mjs:9:36)
at async file:///Users/carlos/Documents/Projetos/Javascript/one/packages/one/dist/esm/createHandleRequest.mjs:82:38
at async file:///Users/carlos/Documents/Projetos/Javascript/one/packages/one/dist/esm/vite/resolveResponse.mjs:10:26
Error rendering / on server
module is not defined
- On native this plugin should be the last plugin run on css files (the
inlineRemis aligned with web expectations, I don't know if this should be an userland option, also for some reason, when I addenforce: post, the code for the id base.css is empty):
{
name: "nativewind:css",
transform(code, id) {
if(["android", "ios"].includes(this.environment.name) && id.endsWith(".css")) {
const data = JSON.stringify(cssToReactNativeRuntime(code, { inlineRem: 16 }))
return { code:`require("nativewind").StyleSheet.registerCompiled(${data})`, map: null }
}
}
}
divs are not supposed to work in react-native when usingreact-native-css-interop
After doing the step 1 modification. I was able to render correctly on web (I also set the native.css to true, but as far as I read this should be automatically set when nativewind exists in package.json right?):
- On tailwindcss configuration,
nativewind/presetshould be added, this adds a@cssInterop nativewind;at-rule which is transformed to a flagnativewind: trueby thecssToReactNativeRuntimefunction later on
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
"./app/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
presets: [require("nativewind/preset")],
plugins: [require("tailwindcss-animate"), require("tailwindcss-motion")],
};
- In order to
nativewind/presetbe correctly picked I've added the following plugin to vite:
{
name: "nativewind:pre",
enforce: 'pre',
buildStart() {
process.env.NATIVEWIND_OS = ["android", "ios"].includes(this.environment.name) ? "native" : "web"
}
}
- After all that, native still doesn't work, but I've think that the css is not correctly being added to JS afterall. Maybe there's another vite plugin that process css inside one (I don't have the sufficient knowledge in one codebase to understand whats going on)
@natew https://github.com/onejs/one/pull/414 My branch pointing to yours
@ceopaludetto thanks for all this, super helpful. I applied most of the changes, got quite a bit further it seems but hitting index.default is not defined as it starts, haven't pinpointed what's going on but it's likely some module compatibility issue.
I'll poke on this more through the day, I didn't see the jsx runtime being swapped in the native bundle either which I thought it was before will need to check.
Ok I have it far along now, I think properly doing everything except vxrn is messing up the bundle of nativewind in the end. despite it using interop settings its leaving some require calls untouched and they break.
a bit more poking - i think nativewind has circular imports, took a while to diagnose. will check into that next.
this works in 1.1.399 but for now i am only compiling non node_modules so nativewind modules may not work, we can enable it but we have to filter out a variety of node modules or else you get breaking circular imports, we just need to make the regex better there to decide when to avoid wrapping.