コンテンツにスキップ
Webフロントエンドエンジニアのための React Native 実践入門
Esc
navigateopen⌘Jpreview
このページの内容

第7章 React Nativeのスタイリング

StyleSheet APIの書き方に加えて、FlexboxのデフォルトがcolumnであることやSafeAreaの扱いなど、Webとずれる部分を確認します。

本章では、コアコンポーネントに見た目を与える方法を扱います。React NativeはCSSを直接使えませんが、JavaScriptオブジェクトとしてスタイルを書けるStyleSheet APIが標準で用意されています。本書ではこれを基本方針とします。最後にコラムとして、Tailwindライクな選択肢にも触れます。

StyleSheet API

最もよく使う書き方を見ます。

import { View, Text, StyleSheet } from "react-native";

function Card() {
  return (
    <View style={styles.card}>
      <Text style={styles.title}>タイトル</Text>
      <Text style={styles.body}>本文</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    padding: 16,
    borderRadius: 8,
    backgroundColor: "#fff",
    gap: 8,
  },
  title: {
    fontSize: 18,
    fontWeight: "bold",
  },
  body: {
    fontSize: 14,
    color: "#666",
  },
});

StyleSheet.createに渡すのは、CSSプロパティ名をキャメルケースに直したオブジェクトです。background-colorbackgroundColorfont-weightfontWeightになります。

StyleSheet.createを使う利点は、

  • TypeScriptで型補完が効く
  • スタイルオブジェクトに対する内部的な最適化が効く

の2点です。インラインで<View style={{ padding: 16 }}>と書いても動きますが、よく使うスタイルはStyleSheet.createにまとめておくのが定石です。

配列でスタイルを合成する

スタイルを複数組み合わせたい場合は、配列にして渡します。

function Card({ isSelected }: { isSelected: boolean }) {
  return <View style={[styles.card, isSelected && styles.cardSelected]} />;
}

後ろに書いたスタイルが優先されます。条件付きスタイルも、配列の中で&&や三項演算子を使えば素直に書けます。

値の単位

第3章で触れたとおり、数値はそのままdpとして扱われます。padding: 16は16dpを意味し、デバイスの密度に応じて自動で実ピクセルに変換されます。

色は"#fff""red""rgba(0, 0, 0, 0.5)"などの文字列です。Webの感覚と同じです。

Flexboxの挙動の違い

第3章でも触れましたが、ここでもう少し具体的に整理します。

デフォルトがcolumn

すべての<View>は最初からdisplay: flex相当で、flex-directionのデフォルトはcolumnです。Webと逆向きなので、横に並べたい時は明示的にrowを指定します。

// 縦に並ぶ(デフォルト)
<View>
  <Text>1</Text>
  <Text>2</Text>
</View>

// 横に並べたい
<View style={{ flexDirection: "row" }}>
  <Text>1</Text>
  <Text>2</Text>
</View>

「縦スクロールが基本のモバイル画面」に最適化された結果です。慣れると縦が基本のほうが楽な場面が多いです。

よく使うプロパティ

レイアウトでよく使うのは次のあたりです。Webと書き方は同じです。

  • flex: 1 ― 親の余白を埋める
  • flexDirection: "row" | "column"
  • justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | "space-evenly" ― 主軸方向の配置
  • alignItems: "flex-start" | "center" | "flex-end" | "stretch" | "baseline" ― 交差軸方向の配置
  • gap: 8 ― 子要素間のすき間(row-gap / column-gapもあります)

ない・限定されているプロパティ

参考までに、Webでは使えるけれどReact Nativeでは使えない/限定的なものを挙げておきます。

  • display: grid系 ― CSS Gridは現状サポートされていません(2026年に向けて議論中)
  • position: fixed ― 存在しない。absoluterelativeは使える
  • inline-block / inline ― displayは基本flexのみ
  • background-image ― 直接は指定できない。画像は<Image>で配置する

「Webと同じプロパティ名で書ける範囲はそのまま使える、そうでないものは別の方法で組む」と覚えておけば十分です。

SafeAreaの扱い

モバイル特有の概念として、SafeArea(ノッチや角丸、ホームインジケータの領域を避ける範囲)があります。

iPhoneの上部のノッチ、Dynamic Island、下部のホームインジケータと重なる位置に重要なUIを置くと、見えなくなったり操作しにくくなったりします。これを避けるためにSafeAreaの概念があります。

SafeAreaを無視した画面と考慮した画面の比較

SafeAreaの外側は表示できない領域ではありませんが、重要な情報や操作UIを置くのには向きません。

SafeAreaView、スクロール領域、固定タブバーでSafe Areaの余白をどう分担するか

Expo Routerを使ったプロジェクトでは、SafeAreaの土台(SafeAreaProvider)がアプリ全体に最初から仕込まれているので、各画面ではSafeAreaViewで囲むだけ、もしくはuseSafeAreaInsetsで値を取って自分で余白に反映するだけで使えます。

importするSafeAreaViewは必ず**react-native-safe-area-context**から取ってください。react-native本体にも同名のコンポーネントがありますが、こちらはiOSのみ対応で挙動が古く、Androidでは効きません。本書も以降はreact-native-safe-area-contextの方を使います。

import { SafeAreaView } from "react-native-safe-area-context";

function Screen() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      {/* この中はSafeArea内に収まる */}
    </SafeAreaView>
  );
}

微調整が必要な場合は、useSafeAreaInsetsで「上下左右のSafeArea余白(insets)」を取り出して、自分でpadding等に反映します。

import { useSafeAreaInsets } from "react-native-safe-area-context";
import { View } from "react-native";

function Screen() {
  const insets = useSafeAreaInsets();
  return <View style={{ paddingTop: insets.top }} />;
}

なお、Expo Routerを使わない構成では、自分でアプリ全体をSafeAreaProviderでラップする必要があります(react-native-safe-area-contextのドキュメント参照)。

プラットフォーム別スタイル

iOSとAndroidで微妙にUIを変えたい場面はよくあります。3つのやり方があります。

Platform.OS で分岐する

import { Platform } from "react-native";

const styles = StyleSheet.create({
  header: {
    paddingTop: Platform.OS === "ios" ? 44 : 24,
  },
});

簡単に分岐できますが、複雑になると読みにくくなります。

Platform.select を使う

const styles = StyleSheet.create({
  header: {
    ...Platform.select({
      ios: { paddingTop: 44, fontFamily: "Helvetica" },
      android: { paddingTop: 24, fontFamily: "Roboto" },
    }),
  },
});

オブジェクト単位で切り替えられます。同じプロパティを複数差し替える場合はこちらが読みやすいです。

.ios.tsx / .android.tsx でファイルごと分ける

ロジックレベルでまったく違う実装を出し分けたい場合は、ファイル名で分岐します。

HeaderBar.ios.tsx
HeaderBar.android.tsx
HeaderBar.tsx        // フォールバック

import HeaderBar from "./HeaderBar"と書くと、Metroが自動で適切なファイルを選んでくれます。コードの差が大きい場合はこの方式が一番すっきりします。

(コラム) Tailwindライクなスタイリング ― NativeWindとUniwind

Web側でTailwind CSSを使い慣れていると、StyleSheet APIに少し物足りなさを感じるかもしれません。React Nativeでも、Tailwindライクな書き心地を提供するライブラリがいくつかあります。

代表的な選択肢は2つです。

NativeWind

Tailwind CSSをReact Nativeで使えるようにする老舗のライブラリです。

import { View, Text } from "react-native";

<View className="p-4 bg-white rounded-lg">
  <Text className="text-lg font-bold">タイトル</Text>
</View>

Tailwindの構文をそのまま使えるので、Web開発の感覚に近いです。v5でTailwind CSS v4対応も進んでいます。Expo公式Skillsのexpo-tailwind-setupはNativeWind v5を扱っています。

Uniwind

Unistylesチームが新しく出している、パフォーマンスを重視したライブラリです。

import { View, Text } from "react-native";

<View tw="p-4 bg-white rounded-lg">
  <Text tw="text-lg font-bold">タイトル</Text>
</View>

書き心地はNativeWindとほぼ同じですが、内部のスタイル解決方式が異なり、より高速で安定したパフォーマンスを出せると言われています。無料版でも十分使え、Pro版もあります。

著者としてはTailwindライクなスタイリングが好みならUniwindを推奨します。理由はパフォーマンスとアーキテクチャの素直さです。NativeWindがすでにチームに馴染んでいるなら無理に乗り換える必要はありません。Uniwindを実際に触った感触はNativeWindの対抗馬?ハイパフォーマンスのUniwindでReact Nativeをスタイリングに書いています。

ここに挙げた以外にもTamaguiなどの選択肢があります。それぞれを実運用したうえでの比較はTamagui を1年運用して分かった、React Native スタイリングの選び方【2025年版】にまとめてあるので、チームで方針を決める前に目を通しておくと判断材料が増えます。

本書の方針

本書のサンプルコードは、原則として標準のStyleSheet APIで進めます。理由は、

  • 依存ライブラリを最小に保てる
  • React Nativeのスタイル概念をそのまま学べる
  • パフォーマンスが安定している

の3点です。Tailwindライクな書き方が好みであれば、各章のStyleSheetをUniwindで書き換える練習として読み替えてください。プロパティ名は同じなので、置き換えはそれほど難しくありません。

(コラム) スタイル定数を活かしたアプローチ ― ALF

NativeWindやUniwindとは別系統のアプローチとして、スタイル定数(atom)を集めた小さなフレームワークを自前で組み、それを使い回す流派もあります。代表例が、SNSアプリBlueskyが採用している**ALF (Application Layout Framework)**です。

ALFは、Tailwind的なUtility First思想をStyleSheet APIの上で表現するもので、atoms.flex_rowt.atoms.bgのような定数を組み合わせてスタイルを当てます。

import { atoms, useTheme } from "#/alf";

function Card() {
  const t = useTheme();
  return <View style={[atoms.flex_row, atoms.p_md, t.atoms.bg]} />;
}

ライブラリではなく、プロジェクト内に小さなフレームワークを作る発想なので、

  • 依存ライブラリを増やさず、StyleSheet APIの素直な使い方の延長で組める
  • テーマ(ライト/ダーク)、ブレークポイント、デザイントークンの管理を一箇所に集約できる
  • ライブラリの破壊的変更に振り回されない

といった利点があります。NativeWindやUniwindほどシンタックスシュガーは強くありませんが、長期メンテナンスを重視するチームに合うアプローチです。実装の参考はBluesky側のソースや、ALFについての解説記事が良い入り口になります。

本章のまとめ

  • StyleSheet APIが基本。プロパティ名はキャメルケース、単位は数値(dp)
  • Flexboxはcolumnがデフォルト。横に並べたい時は明示的にrow
  • SafeAreaはreact-native-safe-area-contextで対応
  • プラットフォーム差はPlatform.OS / Platform.select / .ios.tsx/.android.tsxの3通り
  • TailwindライクならUniwindが現時点の推奨。本書はStyleSheet API基本

次章では、ユーザーからの入力 ― タップとキーボード入力の扱いを見ていきます。

最終更新 2026年7月24日