はじめに
「Webはこのコンポーネント、アプリはあのコンポーネント」と、同じデザインのボタンを2回実装した経験はありませんか?React NativeとWebの両方でサービスを展開していると、UIコードの二重管理は避けて通れない悩みです。デザインを少し変えるたびに2箇所を直し、微妙な見た目の差異に頭を抱える——そんな状況を変えてくれるのが、今回紹介するgluestack-uiです。
gluestack-uiは、ReactとReact Nativeの両方で動くユニバーサルなUIコンポーネント集です。しかも近年主流の「コピー&ペースト型」を採用しており、コンポーネントのコードがそのまま自分のプロジェクトに配置されるため、カスタマイズも自由自在です。
gluestack-uiとは
gluestack-uiは、Tailwind CSS(NativeWind)で作られたReact & React Native向けのコンポーネント&パターン集です。npmパッケージとしてコンポーネントをインポートするのではなく、CLIでコンポーネントのソースコードを自分のプロジェクトにコピーして使う、いわゆるshadcn/uiスタイルの設計を採用しています。
2026年6月にリリースされたv5では、NativeWind v5(Tailwind CSS v4ベース)への対応やExpo Routerとの統合が進み、GitHubスター数は5,000を超える人気プロジェクトに成長しています。
主な特徴
- ユニバーサル対応 - 同じコンポーネントコードがExpo(iOS/Android)とWebの両方で動作します。UIの二重実装から解放されます
- コピー&ペースト型設計 - コンポーネントは
components/ui/配下に実体のコードとして配置されます。ブラックボックスがなく、必要なら中身を直接書き換えられます - Tailwind CSS(NativeWind)ベース - スタイリングは
classNameにTailwindのユーティリティクラスを書くだけ。Web開発者の知識がそのままモバイルに活きます - アクセシビリティ重視 - キーボード操作やスクリーンリーダー対応が組み込み済みで、品質の底上げに貢献します
- 必要な分だけ導入 - ButtonだけほしければButtonだけ追加できます。バンドルに不要なコードが混ざりません
インストール
前提条件はExpo 50以上、React Native 0.72.5以上、Node 16超です。既存のExpoプロジェクトで、まず初期化コマンドを実行します。
npx gluestack-ui@latest init
CLIがスタイリングエンジンの選択を求めてきます。ExpoとReact Native CLIの両方に対応するNativeWind v5(Tailwind CSS v4 + PostCSS)と、Expo専用でPostCSS不要のUniWindから選べます。迷ったらNativeWindで問題ありません。
このコマンド1つで、GluestackUIProviderの配置、metro.config.js・babel.config.js・global.cssの設定まで自動で構成してくれます。あとは使いたいコンポーネントを追加するだけです。
npx gluestack-ui@latest add button
複数まとめて追加することもできます。
npx gluestack-ui@latest add box text input heading vstack hstack
なお、v5は現時点でNext.js未対応です。Next.jsプロジェクトで使う場合はv4のドキュメントを参照してください。また、プロジェクトのディレクトリ名に空白が含まれるとエラーになることがあるため、Expo Appのような名前はExpo-Appに変えておくのが安全です。
基本的な使い方
追加したコンポーネントは@/components/ui/からインポートします。まずは定番のButtonから見てみましょう。
import { Button, ButtonText, ButtonSpinner } from '@/components/ui/button';
export default function SubmitButton() {
return (
<Button action="primary" variant="solid" size="md">
<ButtonText>送信する</ButtonText>
</Button>
);
}
action(primary / secondary / positive / negative)で意味的な色分け、variant(solid / outline / link)で見た目、size(xs〜xl)で大きさを指定します。propsだけで一貫したデザインが得られるのが便利です。
さらに、Tailwindのクラスで細部を上書きできます。
<Button className="rounded-full bg-indigo-600 data-[hover=true]:bg-indigo-700">
<ButtonText className="font-bold">丸いボタン</ButtonText>
</Button>
ローディング状態の表現も、ButtonSpinnerを差し込むだけです。
<Button isDisabled={isLoading}>
{isLoading && <ButtonSpinner />}
<ButtonText>{isLoading ? '処理中...' : '保存'}</ButtonText>
</Button>
このコードがiOS・Android・Webのすべてでそのまま動く、というのがgluestack-uiの核心です。
実践的なユースケース
実際のアプリで頻出するログインフォームを組んでみましょう。レイアウト用のVStack、入力用のInput、見出し用のHeadingを組み合わせます。
npx gluestack-ui@latest add vstack heading input button text
import { useState } from 'react';
import { VStack } from '@/components/ui/vstack';
import { Heading } from '@/components/ui/heading';
import { Input, InputField } from '@/components/ui/input';
import { Button, ButtonText } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
export default function LoginScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const isValid = email.includes('@') && password.length >= 8;
return (
<VStack space="lg" className="flex-1 justify-center p-6">
<Heading size="xl">おかえりなさい</Heading>
<Text className="text-typography-500">
アカウントにログインしてください
</Text>
<Input variant="outline" size="md">
<InputField
placeholder="メールアドレス"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
</Input>
<Input variant="outline" size="md">
<InputField
placeholder="パスワード(8文字以上)"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
</Input>
<Button size="lg" isDisabled={!isValid}>
<ButtonText>ログイン</ButtonText>
</Button>
</VStack>
);
}
ポイントは、この画面がスマホアプリとしてもWebページとしても、同一コードで成立することです。Expo Routerと組み合わせれば、npx expo start --webでブラウザ確認しながら開発し、そのままネイティブアプリとしてビルドできます。
ダークモード対応も標準装備
gluestack-uiのコンポーネントはテーマトークンで色が定義されているため、GluestackUIProviderのmodeを切り替えるだけでダークモードに対応できます。
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
import { useColorScheme } from 'react-native';
export default function App({ children }: { children: React.ReactNode }) {
const colorScheme = useColorScheme();
return (
<GluestackUIProvider mode={colorScheme === 'dark' ? 'dark' : 'light'}>
{children}
</GluestackUIProvider>
);
}
端末のシステム設定に追従するダークモードが、この十数行で実現します。個別のコンポーネントにはdark:プレフィックスのTailwindクラスで微調整も可能です。
コピペ型だからこそのカスタマイズ
コンポーネントの実体はcomponents/ui/button/index.tsxのような形で手元にあります。たとえば自社のデザインシステムに合わせてデフォルトの角丸を変えたい場合、ライブラリのアップデートを待つ必要はなく、ファイルを直接編集すれば完了です。npmパッケージ型のUIライブラリで「あと一歩がカスタマイズできない」と苦しんだ経験がある方ほど、この自由度のありがたみを実感できるはずです。
まとめ
gluestack-uiは、次のような特徴を持つユニバーサルUIライブラリです。
- 同一コードでExpo(iOS/Android)とWebに対応し、UIの二重管理を解消できます
- コピー&ペースト型設計で、コンポーネントの中身まで完全にコントロールできます
- NativeWind(Tailwind CSS)ベースなので、Webの知識がそのまま通用します
- CLIによるセットアップが整備されており、導入の敷居が低いです
「アプリもWebも」が当たり前になった今、UIコンポーネントをプラットフォームごとに作り分けるコストは無視できません。まずはnpx gluestack-ui@latest initで既存のExpoプロジェクトに導入し、Buttonひとつから試してみてください。Webとネイティブで同じボタンが動いた瞬間、開発体験が一段変わるのを感じられるはずです。
