npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-stylesheet-to-tailwind

v0.1.1

Published

Convert React Native StyleSheet.create styles to Tailwind className for Uniwind and NativeWind 5.

Readme

react-native-stylesheet-to-tailwind

A codemod that moves React Native StyleSheet.create styles to Tailwind className strings for Uniwind and NativeWind 5.

Try it in the browser: live playground.

It never guesses. Every class it writes is proven against your own Tailwind theme, so the app looks exactly the same after the run. A style that cannot be proven stays where it is, with a reason.

// before
<Text style={styles.title}>{title}</Text>

const styles = StyleSheet.create({
  title: { marginTop: 2, fontSize: 16, fontWeight: "500", lineHeight: 20.8 },
})

// after
<Text className="mt-0.5 text-[16px] font-medium leading-[20.8px]">{title}</Text>

Run it

npx react-native-stylesheet-to-tailwind            # dry run: shows the diff, writes nothing
npx react-native-stylesheet-to-tailwind --write    # writes the changes (needs a clean git tree)

Run it in the app folder, or pass the folder as the first argument. It finds the library from package.json and the Tailwind CSS entry on its own.

| Flag | What it does | | ----------------------------- | ------------------------------------------------------------------------------- | | --write | Write the changes to the files. | | --yes | Confirm --write when there is no terminal. | | --allow-dirty | Write even when the git tree has changes. | | --json | Print one JSON object. No colors, no prompts. | | --diff | Show the diff also when writing. | | --library <name> | uniwind or nativewind. Found from package.json when left out. | | --css <path> | The Tailwind CSS entry file. Found when left out. | | --class-merge <import path> | Import of your class-merge function, like @/cn. Lets conditional styles move. | | --color-names <path> | JSON file that maps a color path in your code to a theme token name. | | --no-arbitrary-values | Keep styles that would need classes like text-[17px]. | | --include <glob> | Only convert matching files. Repeatable. | | --exclude <glob> | Skip matching files. Repeatable. | | --help, --version | Show help or the version. |

Exit codes: 0 ok, 1 failed, 2 bad usage.

How a class is proven

  1. It loads your Tailwind CSS entry with @tailwindcss/node, so your theme tokens (text-brand, rounded-card) are known.
  2. For each style it picks a candidate class, asks Tailwind for that class's CSS, and turns the CSS back into a React Native style.
  3. The class is written only when that style equals the original exactly. Otherwise the property stays in StyleSheet.create.

A style can move fully, split (some properties move, the rest stay in style), or stay.

Color names

Pass --color-names colors.json to move colors that your code reads from a JS object. The file maps the color path, as written in the source, to a theme token name:

{ "colors.mutedForeground": "ink-soft", "palette.brand.main": "brand" }

With that file, color: colors.mutedForeground becomes text-ink-soft, and backgroundColor and borderColor become bg- and border- classes. If the theme CSS has no --color-ink-soft, the style stays with color-name-not-in-theme. A bad file exits with code 2.

Why a style stays

| Reason | Meaning | | --------------------------------- | ------------------------------------------------------------------------------------------- | | component-has-no-class-name | The component does not take a className. | | dynamic-value | The value is worked out at runtime. | | used-outside-style-prop | The style is also read outside a style prop. | | conditional-without-class-merge | The style is conditional and no class-merge function was found. | | family-conflict | The classes would fight over the same property. | | style-order-would-flip | Moving it would change which style wins. | | no-exact-class | No Tailwind class gives the exact same value. | | color-name-not-in-theme | The color is named in the color names file, but the theme CSS has no such --color- token. | | scheme-wrapper | The styles are built by a function that reads the color scheme. | | no-css-entry-covers-file | No working CSS entry of a Uniwind or NativeWind app covers this file. |

For agents

Moving styles by hand is a poor job for a coding agent: it reads every file, guesses each class, and spends tokens on work a tool can prove. Let the agent run the codemod and read one JSON object instead:

npx react-native-stylesheet-to-tailwind --json
{
  "mode": "dry-run",
  "libraries": ["uniwind"],
  "files": { "scanned": 9115, "changed": 520 },
  "styles": { "moved": 258, "split": 721, "stayed": 2049, "arbitrary": 232 },
  "stays": [
    {
      "file": "apps/web/app-frame.tsx",
      "line": 29,
      "rule": "frameStyles.frameOnWeb",
      "reason": "no-exact-class"
    }
  ]
}

The agent then spends its tokens only on the stays list, where a human decision is needed.

SKILL.md is an agent skill with these steps and what to do for each stay reason. Add it to a project with npx skills add YosefHayim/react-native-stylesheet-to-tailwind.

Benchmark

Two agents (same model, same prompt goal) converted the same 10 StyleSheet files from a real Uniwind monorepo. One edited the files by hand, the other ran this codemod.

| | Manual agent | Codemod agent | Saved | | ------------------------ | ------------ | ------------- | ----- | | Tokens | 50,326 | 25,568 | 49% | | Time | 55.2 s | 18.0 s | 67% | | Tool calls | 8 | 3 | 63% | | Styles moved | 11 | 3 | | | Classes based on a guess | 4 | 0 | |

The manual agent moved more styles, but four of its classes were guesses. It wrote rounded-[10px] for a radius token that each app sets at runtime, and it replaced color-scheme style sets with bg-secondary, border-border and text-foreground without checking that those CSS colors match. The codemod left those styles in place with a reason (scheme-wrapper, dynamic-value, family-conflict), and every class it wrote was exact.

The codemod's cost is mostly the agent's fixed starting context, so the saving grows with the number of files. One run is a single sample, not a study; run your own with --json.

Library API

import { convertProject } from "react-native-stylesheet-to-tailwind";

const projectReport = await convertProject({
  projectFolder: "./apps/mobile",
  flags: {
    write: false,
    yes: false,
    allowDirty: false,
    json: true,
    diff: false,
    library: null,
    cssEntryPath: null,
    classMergeImport: "@/cn",
    allowArbitraryValues: true,
    includePatterns: [],
    excludePatterns: [],
  },
});

Supported

  • Uniwind and NativeWind 5 (Tailwind 4).
  • Not yet: NativeWind 4 (Tailwind 3), useColorScheme to dark:.

Node 20 or newer.

FAQ

No. A class is written only when the style Tailwind makes from it equals the original style exactly. Anything else stays in StyleSheet.create.

Each style that stays is listed with a reason. See Why a style stays. With --json they come in the stays list.

--write refuses to run on a git tree with uncommitted changes, unless you pass --allow-dirty. So the run is one clean diff: review it with git diff and undo it with git restore ..

Yes. Pass --include <glob> and --exclude <glob>, as many times as you need.

When no theme class gives the exact value, an arbitrary value class is the only exact match. Pass --no-arbitrary-values to keep those styles in StyleSheet.create instead.

A style like style={[styles.base, isActive && styles.active]} needs a class-merge function such as cn. The codemod uses the one your files import most, or the one you pass with --class-merge. Without one, the style stays with the reason conditional-without-class-merge.

Yes. Each file is checked against its own package and the Tailwind CSS entry that covers it.

Not yet. NativeWind 4 runs on Tailwind 3, which is not supported. Styles built inside a function that reads the color scheme stay with the reason scheme-wrapper.

No. Everything runs on your machine. The codemod makes no network calls.

Yes. --json prints one JSON object with no colors or prompts, and --write --yes writes without a terminal. See For agents and llms.txt.

License

MIT