className is silently dropped under Vite: no bundler integration exists outside Metro

Ouverte
#405 0 commentaires 1 réaction 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 12 jours

@p-larson y travaille déjà.

Depuis le 6/8/2026.

  • #406 par @p-larson — ouverte

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
22/100
Type d'issue
Fonctionnalité
Clarté
Plutôt claire
Activité
À l'abandon
Stack technique
react-native, typescript, vite
Domaine
build-system

Piste de recherche

Start with the Metro-only nativeResolver in the resolver code, since the proposed react-native-css/vite export reuses its react-native → react-native-css/components mapping, and check isFromThisModule for the skip rule. Read the reproduction repo and the react-native-web alias ordering that vite-plugin-rnw sets up. Done means the Vite export and its esbuild optimizeDeps registration pass the resolver tests, but linked PR #406 already covers this, so coordinate with it first.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

Summary

Under Vite there is nothing that performs the react-native → react-native-css/components swap, so every className is dropped. The page still renders — same DOM, same text, no error, no warning — it is just unstyled. That makes it easy to ship without noticing.

Opening this before a PR, per CONTRIBUTING's guidance to discuss API changes first. I have a working implementation and would be glad to submit it, adjust the approach, or drop it if you would rather solve this differently.

Reproduction: https://github.com/p-larson/nativewind-vite-storybook-repro

Two apps, byte-identical except for one Vite plugin, plus a script that asserts computed styles rather than "did it render":

[broken]  className in DOM: false   computed size: null x null
[fixed]   className in DOM: true    computed size: 320px x 160px
broken fixed
unstyled styled
Why it happens

NativeWind 4 exported a JSX runtime, so the documented Other Bundlers recipe worked anywhere. Its third requirement is no longer satisfiable on v5 — [email protected] exports only ., ./babel, ./metro, ./types, ./theme, and setting jsxImportSource: "nativewind" fails the build:

Error: Missing "./jsx-dev-runtime" specifier in "nativewind" package

The work moved here, into a Metro-only resolver. I understand from the README that Metro is the officially supported bundler and that contributions for others are welcome — this is that contribution.

Proposed approach

Add a react-native-css/vite export applying the mapping nativeResolver already uses:

if (moduleName === "react-native") {
  return resolver(context, `react-native-css/components`, platform);
}

react-native-css/components is already a barrel re-exporting react-native with the wrappers layered on top, so no new module is introduced and the component list cannot drift.

I first tried porting webResolver (rewriting react-native-web/dist/exports/<X>/index.js → components/<X>) and abandoned it:

  1. Rollup resolvers do not run during dependency pre-bundling. Making the rewrite fire requires excluding react-native-web from optimizeDeps, which exposes its CommonJS dependencies to the browser as raw CJS — inline-style-prefixer, fbjs, styleq, and others, each needing its own optimizeDeps.include entry, down to deep paths like inline-style-prefixer/lib/plugins/crossFade.js.

  2. It rewrites react-native-web's own internal imports, producing exactly the initialization cycle in #380.

Following nativeResolver avoids both — react-native-web stays pre-bundled and its internals are never touched.

Two details that are easy to get wrong:

  • Imports originating inside react-native-css must be skipped (mirroring isFromThisModule). The barrel re-exports react-native and each wrapper uses its base component at module scope, so redirecting them recreates the #380 cycle.
  • Both react-native and react-native-web must be matched, because vite-plugin-rnw applies that alias before user plugins run. Matching only react-native silently no-ops.

The mapping needs registering twice: as a Vite plugin for source files, and as an esbuild plugin in optimizeDeps.esbuildOptions.plugins for pre-bundled dependencies, since esbuild does not run Rollup resolvers.

Status

Implemented on p-larson/react-native-css@feat/vite-resolver: yarn lint, yarn typecheck, and the full yarn test suite (1056 passing) are green, with 5 new resolver tests. vite is added as an optional peer dependency so Metro-only users are unaffected. I also packed the branch and ran the reproduction against the real react-native-css/vite export end-to-end.

Happy to open the PR if the approach looks reasonable.

Environment
Package Version
react-native-css 3.0.7
nativewind 5.0.0-preview.4
react-native-web 0.21
vite 7.2
@storybook/react-native-web-vite 10.2
Langage dominant
TypeScript
Étoiles
166
Forks
45
Merge moyen
17 j 22 h
PR mergées (30 j)
3

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de nativewind/react-native-css

Toutes les issues de nativewind/react-native-css

Issues similaires

Plus d'issues TypeScript

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.