Uniwind(ユニウィンド)は、React NativeのViewやTextにclassName="flex-1 p-4"のようなTailwind CSS v4のクラスを書けるようにするスタイリングライブラリです。Unistylesの作者Jacek Pudysz氏らが開発し、無料版はMITライセンスで公開されています。最新版は2026年11月5日公開の1.13.2です。この記事では導入手順に加えて、1.13.0でネイティブのクラス優先順位が変わった点を1.12.2と並べて実測し、NativeWindとの速度差、Pro版の価格と対応版まで整理します。
まとめ:Uniwindの採用・更新前に押さえる6点
- Tailwind CSS v4専用です。v3のままのプロジェクトでは使えません。
- 要件はReact Native 0.81以上・React 19以上。Babelプリセットは不要で、
global.cssとmetro.config.jsの2ファイルで動きます。 - 1.13.0で勝ち方が変わりました。ネイティブでも
className内の順序は無関係になり、1.12.2で青だったbg-white p-4 bg-blue-500は1.13.2では白になります。上書きを前提にした部品はtailwind-mergeを通してください。 hover:・space-y-*・grid-cols-*・group-*・before:は、無料版1.13.2の実測でもネイティブ向けの出力に残りません。truncateは出力されますが省略は効かず、line-clamp-1が代わりになります。- 開発元の公開ベンチマーク(2026年10月)では、2,003個のビューの描画がiOSで56.73ms。NativeWind 4.2.7の138.12msに対して約2.4倍速い結果です。ただし測定は1.12.0で、1.13系の数字ではありません。
- Pro版は1席あたり年99ドルから。最新の1.9.2はReact Native 0.83以上・Expo SDK 55以上が必要で、Expo Goでは動きません。
開発元と版の推移から始めて、導入、1.13の変更点、クラスの対応範囲、移行判断の順に説明します。
Uniwindの開発元・ライセンスと版の推移
無料版はuni-stack/uniwindリポジトリでMITライセンスとして公開されています。npmの作者名は無料版、有料版のuniwind-proともにUnistackです。2025年7月28日に0.0.1が出て、正式版の1.0.0は2025年11月4日に公開されました。直近は1〜3週間おきに更新が続いています。
| 版 | 公開日 | 主な追加 |
|---|---|---|
| 1.0.0 | 2025-11-04 | 正式版 |
| 1.2.0 | 2025-12-08 | Vite(React Native Web)対応 |
| 1.5.0 | 2026-03-05 | TV向けセレクタ(tv: など) |
| 1.9.0 | 2026-06-11 | LayoutDirection(RTL強制) |
| 1.11.0 | 2026-08-17 | filter系クラス・ScopedVariables |
| 1.12.0 | 2026-09-04 | Tailwind 4.3.3の状態セレクタ対応 |
| 1.13.0 | 2026-10-19 | ネイティブのカスケード順に対応 |
| 1.13.2 | 2026-11-05 | 初期テーマの競合状態を修正 |
npmの週間ダウンロード数は、2026年11月1〜7日でuniwindが1,513,264件、nativewindが2,679,709件でした。5週間前の842,955件と2,225,450件から差は約2.6倍から約1.8倍へ縮んでいます。CIの再インストールも数えるため利用者数の比ではありません。使う予定のUIキットがどちらを前提にしているかは別途確かめてください。
Uniwindの導入手順(Expo・ベアReact Native)
動作要件とインストールコマンド
1.13.2のpeerDependenciesはreact >=19.0.0、react-native >=0.81.0、tailwindcss >=4です。React Native 0.81を採用したExpo SDK 54より古い環境には入りません。インストールは2パッケージだけです(公式のQuickstart)。
npm install uniwind tailwindcss
新規プロジェクトならnpx create-expo-app -e with-router-uniwindでExpo Router入りのテンプレートを使えます。npx create-better-t-stack@latest --template uniwindでも作成できます。Better-T-Stackの位置付けはBetter-T-Stackの解説記事にまとめました。
global.cssとmetro.config.jsの設定
Tailwindの読み込み口になるCSSファイルを、プロジェクトのルートに作ります。
@import 'tailwindcss';
@import 'uniwind';
次にmetro.config.jsをwithUniwindConfigで包みます。Expoの例です(ベアReact Nativeは1行目が@react-native/metro-configになります)。
const { getDefaultConfig } = require('expo/metro-config');
const { withUniwindConfig } = require('uniwind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
cssEntryFile: './global.css',
dtsFile: './src/uniwind-types.d.ts',
});
最後に、アプリのルートコンポーネント(Expo Routerならapp/_layout.tsx)でimport '../global.css'します。公式ドキュメントが明記している落とし穴は次の4つです。
cssEntryFileはプロジェクトルートからの相対パス文字列で書きます。path.resolve()や絶対パスは不可です。withUniwindConfigは一番外側に置きます。他のMetroラッパーの内側に入れると動きません。registerRootComponentを呼ぶindex.tsでglobal.cssを読み込むと、CSSを変えるたびにホットリロードではなく全体の再読み込みになります。- Tailwindがクラス名を探し始める位置は
global.cssの置き場所で決まります。app/global.cssのように下層に置くと、components/のクラスが拾われません。拾わせるには@sourceで追加します。
Uniwindの型定義生成とCIでの事前生成
dtsFileに指定したuniwind-types.d.tsは、Metroを起動したときに自動生成されます。生成前はclassNameの型エラーが出るので、まずnpx expo startを一度走らせてください。CIでMetroより先に型が要る場合は、npx uniwind generate-artifacts --css ./global.css --dts ./src/uniwind-types.d.tsで生成できます。extraThemesでテーマを足しているなら、同じ名前を--themeで渡します。
classNameの書き方とサードパーティ部品への適用
標準コンポーネントとaccent-接頭辞
1.13.2のパッケージには、View・Text・Pressable・FlatList・TextInputなど22個のReact Native標準コンポーネントのラッパーが入っています。import { View } from 'react-native'のままでclassNameが効くのはこのためです。注意が要るのは、styleではなく色の文字列を受け取るpropです。
<ActivityIndicator colorClassName="accent-blue-500 dark:accent-blue-300" />
<TextInput placeholderTextColorClassName="accent-gray-400" />
<Switch thumbColorClassName="accent-white" trackColorOnClassName="accent-green-500" />
色を渡すpropではaccent-接頭辞が必須です。colorClassName="blue-500"と書くと、1.6.2以降は開発コンソールに警告が出ます。
withUniwindによるサードパーティ部品へのclassName対応追加
react-native-safe-area-contextのSafeAreaViewのように、標準コンポーネントを内部で使っていない部品にはclassNameが効きません。その場合はwithUniwindで包みます。
import { withUniwind } from 'uniwind';
import { SafeAreaView } from 'react-native-safe-area-context';
const StyledSafeAreaView = withUniwind(SafeAreaView);
<StyledSafeAreaView className="flex-1 bg-white dark:bg-gray-900" />
名前にstyleかcolorを含むpropは自動で対応付けられ、styleはclassNameに、tintColorはtintColorClassNameになります。数値を受けるpropは第2引数のfromClassNameとstylePropertyで対応付けます。UIキットは、公式FAQがshadcn/ui系のReact Native ReusablesとHeroUI Nativeを挙げています。
1.13.0で変わったクラスの優先順位:1.12.2との実測比較
公式のStyle Specificityページによると、1.13.0(Pro 1.9.0)からネイティブの競合解決はブラウザと同じ順番になりました。比べるのは重要度(!)、カスケードレイヤー、セレクタの詳細度、CSS内の出力順の順で、classNameに書いた順序はここに入りません。1.12.2までは、ネイティブだけ「後に書いたクラスが勝つ」動きでした。
公式は注記のみなので、どこがどう変わるかを両方の版で確かめました。uniwind 1.12.2と1.13.2をそれぞれ独立した環境に入れ、tailwindcss 4.3.5でコンパイルしたスタイルシートを、パッケージ内のネイティブ用ストア(UniwindStore)に読ませて解決結果を取りました。react-nativeは差し替えたNode上の確認で、端末では実行していません。iOS向けとAndroid向けの結果は同じでした。
| className | 1.12.2 | 1.13.2 |
|---|---|---|
| text-red-500 text-blue-500 | 青 | 赤 |
| text-blue-500 text-red-500 | 赤 | 赤 |
| bg-white p-4 bg-blue-500 | 青 | 白 |
| p-8 p-2 | padding 8 | padding 32 |
| card bg-teal-500(cardは層の外) | teal | 白(card) |
| bg-teal-500 card2(card2はcomponents層) | 黒(card2) | teal |
| bg-pink-500! bg-blue-500 | pink | pink |
| active:bg-green-500 bg-yellow-500(押下中) | 緑 | 緑 |
影響が大きいのは3行目と4行目の形です。部品の基底クラスに呼び出し側のclassNameを連結する次のような書き方は、1.12.2なら呼び出し側が勝っていましたが、1.13.2ではTailwindの出力順しだいで基底クラスが勝ちます。bg-whiteがbg-blue-500より後に出力されるからです。エラーも警告も出ないまま見た目だけが変わります。
// 1.13以降は呼び出し側の上書きが保証されない
const CardBefore = ({ className }) => <View className={`bg-white p-4 ${className}`} />;
// 事前に npm install tailwind-merge を実行
// tailwind-merge で競合を先に取り除く
import { twMerge } from 'tailwind-merge';
const Card = ({ className }) => <View className={twMerge('bg-white p-4', className)} />;
twMergeを通したp-4 bg-blue-500は両方の版で青になり、p-8とp-2の組も8に揃いました。1.13以降へ更新するときは、classNameをテンプレート文字列で連結している箇所を全文検索し、基底クラスと同じプロパティを呼び出し側が渡す部品だけtwMergeに置き換えます。
global.cssの独自クラスも逆転します。@layerの外に書いた.cardはユーティリティに常に勝つようになるので、card bg-teal-500で背景を変えたいなら、.cardを@layer componentsの中へ移してください。!付きのbg-pink-500!と押下中のactive:は版を問わず同じ結果でした。インラインのstyleは!付きのクラスより常に優先されます。
ネイティブで効かないクラス:1.13.2のコンパイラで実測
公式の対応一覧には「ほとんどのTailwindクラスはそのまま動く」とあります。ただ、どのクラスが何に変換されるかまでは書かれていません。同じ環境で1.13.2のCSSコンパイラをiOS・Android向けに実行し、スタイルシートに残るクラスと解決結果を確かめました。
| クラス | 解決結果 | 判定 |
|---|---|---|
| p-4 / gap-2 | padding 16 / gap 8 | 有効(rem=16) |
| p-4(rem: 14指定) | padding 14 | 有効 |
| w-[50vw] | width 195(幅390) | 有効 |
| pt-safe-or-4 | max(上端inset, 16) | 有効 |
| line-clamp-2 | WebkitLineClamp 2 | Textで有効 |
| ios:pt-12 | iOSのみ | Androidは出力なし |
| space-y-4 / space-x-2 / divide-y | なし | 出力なし |
| grid-cols-2 | なし | 出力なし |
| hover:text-red-500 | なし | 出力なし |
| group-active:bg-red-500 | なし | 出力なし |
| before:content-[‘x’] / placeholder: | なし | 出力なし |
| backdrop-blur-sm | 空のスタイル | 無効 |
| truncate | textOverflow ほか | 出力されるが省略なし |
| grid / float-left / columns-2 | display:grid など | 出力されるが無効 |
まず押さえたいのはspace-y-*です。公式一覧の「Spacing」に載っているのに、1.13.2でも出力されません。Tailwind v4が子要素セレクタで生成するため、要素ごとのスタイルに落とせないからです。縦の余白は親にgap-4を付けて代用します。
次に、出力されても効かない組があります。grid・float-left・columns-2はスタイルに残りますが、React Native 0.88.1のdisplayの型はnone・flex・contentsだけで、floatやcolumnsの定義もありません。公式もgrid-*を作業中としています。2列の並びはflex-row flex-wrapとw-1/2で組みます。truncateもtextOverflowが同じ型に無いので省略されません。1行の省略はline-clamp-1かnumberOfLines={1}を使います。TextのラッパーがWebkitLineClampをnumberOfLinesへ渡す実装になっているためです。
ios:やandroid:付きのクラスは、別プラットフォームのバンドルから丸ごと除かれます。コンパイルの段階ではどのクラスでもエラーや警告は出ないので、Webと同じコンポーネントを流用すると、余白やグリッドが気付かないまま消えます。移植時はspace-・divide-・grid-cols-・hover:・group-・truncateを全文検索してから置き換えるのが確実です。
テーマとダークモードの切り替え
Uniwindにはlight・dark・systemの3テーマが最初から登録されています。dark:bg-gray-900と書けば端末の設定に追従し、ThemeProviderで包む必要はありません。手動で切り替えるときはUniwind.setTheme('dark')を呼びます。このとき内部でReact NativeのAppearance.setColorSchemeも呼ばれるので、AlertなどのネイティブUIも同じ配色になります。systemからlightやdarkへ切り替えると端末設定への追従は止まり、戻すにはUniwind.setTheme('system')を呼びます。
ブランド別の配色など3つ目以降のテーマは、global.cssで@variantごとにCSS変数を定義し、metro.config.jsのextraThemesに名前を登録します。
@layer theme {
:root {
@variant light { --color-brand: #2563eb; }
@variant dark { --color-brand: #93c5fd; }
@variant ocean { --color-brand: #0e7490; }
}
}
全テーマで同じ変数を定義しておく必要があり、欠けていれば開発ビルドで警告が出ます。現在のテーマはuseUniwind()のthemeで取れます。React Navigationのテーマはヘッダーなどの色を担う別の仕組みなので、そのまま残します。
プラットフォーム別スタイル・セーフエリア・フォント
ios:・android:・native:・web:セレクタ
className="ios:pt-12 android:pt-6 web:pt-4"のように、接頭辞でプラットフォームごとのスタイルを書き分けられます。Platform.select()を書く必要はありません。iOSとAndroidで同じ値ならnative:にまとめられます。TV向けのtv:はmetro.config.jsのisTV: trueが前提です。
p-safeなどのセーフエリアクラス
p-safe・pt-safe・pt-safe-or-4(insetと16の大きい方)・pb-safe-offset-4(inset+16)でノッチやホームインジケーターを避けられます。ただし無料版は、inset値を自分で渡す必要があります。
import { SafeAreaListener } from 'react-native-safe-area-context';
import { Uniwind } from 'uniwind';
<SafeAreaListener onChange={({ insets }) => Uniwind.updateInsets(insets)}>
{children}
</SafeAreaListener>
この設定を忘れるとinsetは0のままです。Pro版ではC++層からinsetが自動で注入されるので、このコードは要りません。
カスタムフォントの指定
React Nativeはフォントのフォールバックに対応していないため、@themeには実際のフォントファイル名を1つだけ書きます。--font-bold: 'Roboto-Bold';のように定義するとfont-boldでそのフォントが使われます。['Roboto-Bold', 'sans-serif']のように並べる書き方は効きません。静的フォントでは太さごとに別ファイルを読み込みます。可変フォントでは、React Native 0.88以降で対応するfontVariationSettingsを使って軸の値を指定できます。
NativeWindとの違いと移行手順
描画速度とテーマ切り替えの比較
開発元が公開しているuniwind-benchmarksの測定条件と結果(2026年10月1〜12日計測)です。リリースビルド、Hermes、New Architecture、React Native 0.86.3で計2,003個のビューを描画し、各起動で10回測った平均を取り、ウォーム起動6回の中央値を公表しています。実機ではなくiOSシミュレータとAndroidエミュレータの結果です。
| ライブラリ | 描画 iOS | 描画 Android | テーマ切替 iOS | テーマ切替 Android |
|---|---|---|---|---|
| StyleSheet(基準) | 37.28ms | 49.74ms | 対象外 | 対象外 |
| Uniwind 1.12.0 | 56.73ms | 68.30ms | 93.19ms | 59.18ms |
| Uniwind Pro 1.8.1 | 45.84ms | 58.24ms | 7.72ms | 7.14ms |
| Tamagui 3.0.3 | 73.08ms | 85.90ms | 25.92ms | 34.53ms |
| NativeWind 4.2.7 | 138.12ms | 160.37ms | 127.57ms | 123.33ms |
| NativeWind 5.0.0-rc.0 | 322.69ms | 329.46ms | 211.69ms | 207.79ms |
描画ではUniwind無料版がNativeWind 4.2.7よりiOSで約2.4倍、Androidで約2.3倍速く、公式の「2倍速い」はこの範囲に収まります。iOSのテーマ切り替えでは差が約1.4倍に縮みます。無料版もNativeWindも、スタイル付きのコンポーネントをすべて再レンダリングするためです。Pro版の7.72msはC++側でビューを直接更新した結果で、テーマ切り替えだけならTamaguiも無料版より速い数字です。測ったのは開発元自身で版も1.12.0のままなので、1.13.0で増えた優先順位の比較の影響はこの表から読めません。自分のアプリでも測ってください。
NativeWindから移行するときの変更点
公式の移行ガイドは15ステップですが、作業の中心は次の5点です。
- Tailwindをv4へ上げる:
tailwind.config.jsは削除し、テーマ定義はglobal.cssの@themeへ移します。v3からv4の破壊的変更はTailwind CSS v3とv4の違いで確認できます。 - Babelプリセットと
nativewind.d.tsを消す:babel.config.jsからnativewind/babelを外します。 - remの基準値が変わる:NativeWindは14px、Uniwindは16pxです。同じ
p-4でも14から16に広がります。見た目を保つならpolyfills: { rem: 14 }を指定します(上の実測表のとおり、この指定でp-4は14になります)。 vars()とcssInteropを置き換える:JSで定義していたテーマ変数は@variant付きのCSS変数に、cssInteropはwithUniwindに移します。- アニメーション系クラスを書き直す:NativeWindの
animate-*に当たる変換層は無料版に無いため、Reanimatedの書き方に直します。
重複クラスの解決も移行時の確認項目です。NativeWindと違ってUniwindはclassNameの重複を自動で解消しないので、前章のtwMergeへの置き換えを先に済ませてください。移行手順をまとめたAIエージェント用のスキルmigrate-nativewind-to-uniwindも公式が配布しています。
NativeWind側の版の構成(4.2.7はTailwind v3、5.0はRC段階でTailwind v4)はNativeWindの解説記事にまとめています。Tailwind v3のまま安定運用したいならNativeWind 4.2.7に留まり、v4へ上げる作業をするならUniwindへ移るのが素直な分岐です。
Uniwind Proと無料版の違い・価格
Pro版は無料版のクラス対応をそのまま引き継ぎ、Unistyles由来のC++エンジンを足した有料版です。npmのuniwind-proの最新は1.9.2で、ライセンスは商用です。導入時はpackage.jsonのuniwindをnpm:uniwind-pro@latestへ差し替えるエイリアス方式で、import元はuniwindのままです。認証とBabelの設定を済ませたら、ネイティブアプリを再ビルドします。Metroの再起動だけでは反映されません。
| 項目 | 無料版 1.13.2 | Pro版 1.9.2 |
|---|---|---|
| React Native | 0.81以上 | 0.83〜0.89 |
| Expo SDK | 54以上 | 55〜58 |
| Expo Go | 動作する | 不可(開発ビルド必須) |
| テーマ変更時 | 再レンダリング | 再レンダリングなし |
| className でアニメーション | 不可 | Reanimated 4で可 |
| group-active: など | 効果なし | 対応 |
| セーフエリア | SafeAreaListener が必要 | 自動注入 |
| テーマ切替アニメーション | なし | フェード・円形など |
無料版より下限が高い点に注意してください。Proの対応範囲は公式の互換表のとおり1.8.0で0.83以上に上がったため、Expo SDK 54のプロジェクトは無料版なら入りますが最新のProは入りません。必要な依存はreact-native-reanimated 4.0.0以上、react-native-worklets 0.6.0以上、react-native-nitro-modules 0.37.2以上です。Reanimated 4の要件はReact Native Reanimatedの解説で確認できます。
価格は税別・年額の席課金で、1〜3席目が1席99ドル、4〜6席目が49ドル、7〜15席目が29ドル、16席目以降が1ドルです(公式の価格ページ)。席ごとの個人ライセンスは月300回までダウンロードでき、2人以上の組織はプロジェクト単位のチームライセンス(月1,000回)を作れます。CI用ライセンス(月1,000回)は購入に含まれます。利用規約の7.4条では、契約が切れても配布済みのアプリは動き続けますが、新しいビルドの作成と更新の受け取りはできません。リリースを続ける限り課金が続く前提で見積もってください。
Proが元を取れるのは、長いリストやライブ更新の画面でテーマ切り替え時にカクつく場合と、ホワイトラベル製品で複数テーマを滑らかに切り替える場合です。どちらの問題も起きていなければ無料版で足ります。className経由のアニメーションやgroup系が要件にあるならProが必要です。
Uniwindの採用を見送る条件
次のどれかに当てはまる場合は、切り替えを見送ってください。
- Expo SDK 53以前・React 18から上げられない:peerDependenciesを満たせず、インストールで止まります。先にExpoのSDK更新が必要です。
- Next.jsとコンポーネントを共有したい:公式はMetroとVite専用で、Next.jsは非対応と明言しています。コミュニティ製の
uniwind-plugin-nextはありますが実験段階です。 - Web側でhoverやCSS Gridに頼るUIを共通化している:前述の実測どおりネイティブでは消えるので、共通化の利点が薄れます。Web出力の仕組みはReact Native Webの解説を参照してください。
- Tailwind v3のプラグインやpresetに依存している:v4への移行作業そのものが重く、ライブラリ交換と同時に行うと原因の切り分けが難しくなります。
新規のExpoプロジェクトでTailwind v4を使い、必要なクラスとUIキットの対応を確認できるなら、Uniwindを第一候補にします。既存アプリの移行や技術選定を外部に任せたい場合は、当社のFlutter・React Native開発支援でも相談を受けています。
よくある質問
UniwindはExpo Goで動きますか?
無料版は動きます。ネイティブコードを含まないため、npx expo startからそのままExpo Goで確認できます。Pro版はC++モジュールを含むので動きません。EAS Buildやベアプロジェクトで開発ビルドを作ってください。無料版で試作してProへ切り替える場合は、切り替えの時点で開発ビルドの準備が要ります。
Uniwindのnpmパッケージ名と最新版は?
無料版のパッケージ名はuniwindで、2026年11月時点の最新は1.13.2です。tailwindcssと一緒にnpm install uniwind tailwindcssで入れます。Pro版は別パッケージのuniwind-proで最新は1.9.2です。こちらはインストール時にライセンス認証が走るため、CIではUNIWIND_AUTH_TOKENの設定が必要です。
Uniwind Proは購入前に試せますか?
公式のデモアプリuniwind-pro-demoで試せます。iOSシミュレータ向けのビルド済みフレームワークが同梱され、アカウントや購入は不要です。同梱の.tgzをExpo SDK 56・React Native 0.85系の手元のアプリへ入れて試すこともできます。ただしデモの版はPro 1.6.0なので、最新の1.9.2で変わった挙動は確かめられません。
UniwindとNativeWindはどちらを選べばよいですか?
Tailwind v4で進めるならUniwindを選びます。Tailwind v3の資産を変えずに安定版を使いたいならNativeWind 4.2.7です。NativeWind 5はTailwind v4に対応しますが、2026年11月時点でRC段階にとどまり、公式ベンチマークのrc.0は描画が4.2.7より遅い結果でした。既存アプリを移すときは、remの基準値とclassNameの重複解決の2点を先に確認してください。
Uniwindでp-safeが効かないのはなぜですか?
無料版では、react-native-safe-area-contextのSafeAreaListenerからUniwind.updateInsets(insets)を呼ぶ必要があります。呼んでいないとinsetが0のままになり、pt-safe-or-4のような指定は16だけが効いて、ノッチを避けられません。このリスナーをアプリのルート付近に一度置けば、以降の画面にも反映されます。Pro版は自動注入なので不要です。
関連記事
- NativeWindとは?React NativeでTailwind CSSを使う導入手順とv4・v5の違い【2026年11月時点】
- Tailwind CSS v3とv4の違い|破壊的変更と移行手順をv4.3の実ビルドで検証【2026年11月】
- Expo(React Native)とは?Expo Go・開発ビルド・EASの役割と採用判断を実装目線で解説【2026年版】
- React Native Reanimatedとは?UIスレッド駆動の仕組みと4系の版の縛り【2026年8月時点】
- React Native Webとは?DOM変換の仕組みとExpo Web構成・更新停滞下の採用判断【2026年8月時点】