migrate-nativewind-to-uniwind
Migrate a React Native project from NativeWind to Uniwind. Use when the user wants to replace NativeWind with Uniwind, upgrade from NativeWind, switch to Uniwind, or mentions NativeWind-to-Uniwind migration. Handles package removal, config migration, Tailwind 4 upgrade, cssInterop removal, theme conversion, and all breaking changes.
What this skill does
# Migrate NativeWind to Uniwind
Uniwind replaces NativeWind with better performance and stability. It requires **Tailwind CSS 4** and uses CSS-based theming instead of JS config.
## Pre-Migration Checklist
Before starting, read the project's existing config files to understand the current setup:
- `package.json` (NativeWind version, dependencies)
- `tailwind.config.js` / `tailwind.config.ts`
- `metro.config.js`
- `babel.config.js`
- `global.css` or equivalent CSS entry file
- `nativewind-env.d.ts` or `nativewind.d.ts`
- Any file using `cssInterop` or `remapProps` from `nativewind`
- Any file importing from `react-native-css-interop`
- Any ThemeProvider from NativeWind (`vars()` usage)
## Step 1: Remove NativeWind and Related Packages
Uninstall ALL of these packages (if present):
```bash
npm uninstall nativewind react-native-css-interop
# or
yarn remove nativewind react-native-css-interop
# or
bun remove nativewind react-native-css-interop
```
**CRITICAL**: `react-native-css-interop` is a NativeWind dependency that must be removed. It is commonly missed during migration. Search the entire codebase for any imports from it:
```bash
rg "react-native-css-interop" -g "*.{ts,tsx,js,jsx}"
```
Remove every import and usage found.
## Step 2: Install Uniwind and Tailwind 4
```bash
npm install uniwind tailwindcss@latest
# or
yarn add uniwind tailwindcss@latest
# or
bun add uniwind tailwindcss@latest
```
Ensure `tailwindcss` is version 4+.
## Step 3: Update babel.config.js
Remove the NativeWind babel preset:
```js
// REMOVE this line from presets array:
// 'nativewind/babel'
```
No Uniwind babel preset is needed.
## Step 4: Update metro.config.js
Replace NativeWind's metro config with Uniwind's. `withUniwindConfig` must be the **outermost wrapper**.
**Before (NativeWind):**
```js
const { withNativeWind } = require('nativewind/metro');
module.exports = withNativeWind(config, { input: './global.css' });
```
**After (Uniwind):**
```js
const { getDefaultConfig } = require('expo/metro-config');
// For bare RN: const { getDefaultConfig } = require('@react-native/metro-config');
const { withUniwindConfig } = require('uniwind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
});
```
`cssEntryFile` must be a **relative path string** from project root (e.g. `./global.css` or `./app/global.css`).
Do **not** use absolute paths or `path.resolve(...)` / `path.join(...)` for this option.
```js
// ❌ Broken
cssEntryFile: path.resolve(__dirname, 'app', 'global.css')
// ✅ Correct
cssEntryFile: './app/global.css'
```
**Always set `polyfills.rem` to 14** to match NativeWind's default rem value and prevent spacing/sizing differences after migration.
If the project uses custom themes beyond `light`/`dark` (e.g. defined via NativeWind's `vars()` or a custom ThemeProvider), register them with `extraThemes`. Do NOT include `light` or `dark` — they are added automatically:
```js
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
polyfills: { rem: 14 },
extraThemes: ['ocean', 'sunset', 'premium'],
});
```
Options:
- `cssEntryFile` (required): relative path string to CSS entry file (from project root)
- `polyfills.rem` (required for migration): set to `14` to match NativeWind's rem base
- `extraThemes` (required if project has custom themes): array of custom theme names — do NOT include `light`/`dark`
- `dtsFile` (optional): path for generated TypeScript types, defaults to `./uniwind-types.d.ts`
- `debug` (optional): log unsupported CSS properties during dev
## Step 5: Update global.css
Replace NativeWind's Tailwind 3 directives with Tailwind 4 imports:
**Before:**
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
**After:**
```css
@import 'tailwindcss';
@import 'uniwind';
```
## Step 6: Update CSS Entry Import
Ensure `global.css` is imported in your main App component (e.g., `App.tsx`), NOT in the root `index.ts`/`index.js` where you register the app — importing there breaks hot reload.
## Step 7: Delete NativeWind Type Definitions
Delete `nativewind-env.d.ts` or `nativewind.d.ts`. Uniwind auto-generates its own types at the path specified by `dtsFile`.
## Step 8: Delete tailwind.config.js
Remove `tailwind.config.js` / `tailwind.config.ts` entirely. All theme config moves to CSS using Tailwind 4's `@theme` directive.
Migrate custom theme values to `global.css`:
**Before (tailwind.config.js):**
```js
module.exports = {
theme: {
extend: {
colors: {
primary: '#00a8ff',
secondary: '#273c75',
},
fontFamily: {
normal: ['Roboto-Regular'],
bold: ['Roboto-Bold'],
},
},
},
};
```
**After (global.css):**
```css
@import 'tailwindcss';
@import 'uniwind';
@theme {
--color-primary: #00a8ff;
--color-secondary: #273c75;
--font-normal: 'Roboto-Regular';
--font-bold: 'Roboto-Bold';
}
```
Font families must specify a **single font** — React Native doesn't support font fallbacks.
## Step 9: Remove ALL cssInterop and remapProps Usage
**This is the most commonly missed step.** Search the entire codebase:
```bash
rg "cssInterop|remapProps" -g "*.{ts,tsx,js,jsx}"
```
Replace every `cssInterop()` / `remapProps()` call with Uniwind's `withUniwind()`:
**Before (NativeWind):**
```tsx
import { cssInterop } from 'react-native-css-interop';
import { Image } from 'expo-image';
cssInterop(Image, { className: 'style' });
```
**After (Uniwind):**
```tsx
import { withUniwind } from 'uniwind';
import { Image as ExpoImage } from 'expo-image';
export const Image = withUniwind(ExpoImage);
```
`withUniwind` automatically maps `className` → `style` and other common props. For custom prop mappings:
```tsx
const StyledProgressBar = withUniwind(ProgressBar, {
width: {
fromClassName: 'widthClassName',
styleProperty: 'width',
},
});
```
Define wrapped components at **module level** (not inside render functions). Each component should only be wrapped once:
- **Used in one file only** — define the wrapped component in that same file:
```tsx
// screens/ProfileScreen.tsx
import { withUniwind } from 'uniwind';
import { BlurView as RNBlurView } from '@react-native-community/blur';
const BlurView = withUniwind(RNBlurView);
export function ProfileScreen() {
return <BlurView className="flex-1" />;
}
```
- **Used across multiple files** — wrap once in a shared module and re-export:
```tsx
// components/styled.ts
import { withUniwind } from 'uniwind';
import { Image as ExpoImage } from 'expo-image';
import { LinearGradient as RNLinearGradient } from 'expo-linear-gradient';
export const Image = withUniwind(ExpoImage);
export const LinearGradient = withUniwind(RNLinearGradient);
```
Then import from the shared module everywhere:
```tsx
import { Image, LinearGradient } from '@/components/styled';
```
Never call `withUniwind` on the same component in multiple files — wrap once, import everywhere.
**IMPORTANT**: Do NOT wrap components from `react-native` or `react-native-reanimated` with `withUniwind` — they already support `className` out of the box. This includes `View`, `Text`, `Image`, `ScrollView`, `FlatList`, `Pressable`, `TextInput`, `Animated.View`, etc. Only use `withUniwind` for **third-party** components (e.g. `expo-image`, `expo-linear-gradient`, `@react-native-community/blur`).
**IMPORTANT — accent- prefix for non-style color props**: React Native components have props like `color`, `tintColor`, `backgroundColor` that are NOT part of the `style` object. To set these via Tailwind classes, use the `accent-` prefix with the corresponding `*ClassName` prop:
```tsx
// color prop → colorClassName with accent- prefix
<ActivityIndicator
className="m-4"
size="large"
colorClassName="accent-blue-500 dark:accent-blue-400"
/>
// color prop on Button
<Button
colorClassName="accent-background"Related in Ads & Marketing
ads
IncludedMulti-platform paid advertising audit and optimization skill. Analyzes Google, Meta, YouTube, LinkedIn, TikTok, Microsoft, and Apple Ads. 250+ checks with scoring, parallel agents, industry templates, and AI creative generation.
banana
IncludedAI image generation Creative Director powered by Google Gemini Nano Banana models. Use this skill for ANY request involving image creation, editing, visual asset production, or creative direction. Triggers on: generate an image, create a photo, edit this picture, design a logo, make a banner, visual for my anything, and all /banana commands. Handles text-to-image, image editing, multi-turn creative sessions, batch workflows, and brand presets.
rpg-migration-analyzer
IncludedAnalyzes legacy RPG (Report Program Generator) programs from AS/400 and IBM i systems for migration to modern Java applications. Extracts business logic from RPG III/IV/ILE source code, identifies data structures (D-specs), file operations (F-specs), program dependencies (CALLB/CALLP), and converts RPG constructs to Java equivalents. Generates migration reports, complexity estimates, and Java implementation strategies with POJO classes, JPA entities, and service methods. Use when modernizing AS/400 or IBM i legacy systems, analyzing RPG source files (.rpg, .rpgle, .RPGLE), converting RPG to Java, mapping data specifications to Java classes, planning legacy system migration, or when user mentions RPG analysis, Report Program Generator, RPG III/IV/ILE, AS/400 modernization, IBM i migration, packed decimal conversion, or mainframe application rewrite.
brand-library-architect
IncludedBuild a complete brand library for a product — visual asset render pipeline, brand documentation set (BRAND, COPY, MANIFESTO, BIOS, FAQ, GLOSSARY, TONE, PRICING), open-source convention files (README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT), and a self-contained press kit. This skill should be used when the user asks to "build a brand library / brand kit / press kit / brand assets" for a product, "set up a brand library workflow," "create a positioning manifesto plus visual identity," or any combination of brand documentation + visual asset pipeline. Apply phase-by-phase or run end-to-end. Templates are product-agnostic and use {{TOKEN}} placeholders the skill prompts the user to fill.
writing-tech-post
IncludedAuthors engineering blog posts end-to-end: launch deep-dives, incident postmortems, architecture migrations, performance case studies, tutorials, AI/agent system writeups, security disclosures, and research-to-product translations. Picks the correct archetype, plans the abstraction ladder, enforces an evidence cadence (diagrams, benchmarks, profiles, traces, code, ablations), tunes voice against publisher house styles (Datadog, Vercel, GitHub, AWS, Meta, Cloudflare, Jane Street), and runs a pre-publish gate for narrative momentum and disclosure ethics. Use when drafting a new engineering post, restructuring a draft that feels flat, deciding which evidence form belongs where, validating that depth and product context are balanced, or preparing a postmortem, migration, or performance narrative for external publication. Do not use for API reference documentation, README authoring, marketing copy, release notes, generic SEO content, ghost-written executive thought leadership, or non-engineering long-form essays.
blog-google
IncludedGoogle API integration for blog performance: PageSpeed Insights, CrUX Core Web Vitals with 25-week history, Search Console performance, URL Inspection, Indexing API, GA4 organic traffic, NLP entity analysis for E-E-A-T, YouTube video search for embedding, and Google Ads Keyword Planner. Progressive feature availability based on credential tier (API key, OAuth/service account, GA4, Ads). Shares config with claude-seo at ~/.config/claude-seo/google-api.json. Use when user says "google data", "page speed", "core web vitals", "search console", "indexation", "GA4", "keyword research", "nlp entities", "blog performance", "youtube search", "google api setup".