Tailwind CSS

Uniwindとは?React NativeでTailwind CSS v4を使う導入手順と1.13の変更点・NativeWind/Pro版の違い

Uniwindとは?React NativeでTailwind CSS v4を使う導入手順と1.13の変更点・NativeWind/Pro版の違い

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版は自動注入なので不要です。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.11 テックブログ Microsoft 2026年11月の月例更新:Outlookの悪用済みCVE-2026-124194と修正版の確認方法
  2. 2026.11.11 テックブログ JANISの不正アクセス:最大11,652名の医療機関情報と情報窃取プログラムの検知設定
  3. 2026.11.11 テックブログ SMBC日興証券の不正アクセス:持株会会員最大5.6万人の範囲と受託システムの分離設計
  4. 2026.01.21 コラム 税務調査の録音は禁止?判例・裁決と国税局マニュアルで見る、断られたときの対応
  5. 2026.11.09 テックブログ ステップゴルフ不正アクセス:口座情報の対象確認と偽連絡・引き落としの注意点

RELATED POSTS 関連記事

目次