ステータスバーに文字が被るんですけど…React NativeのSafeAreaViewで一発解決する方法

目次

「ステータスバーに文字が被ってるんですけど…」って話

React Nativeでアプリを作り始めて、最初の画面を実機で確認した瞬間、たぶん多くの人がこう思うはずです。

あれ?画面の上、文字がステータスバーに被ってない?

iPhoneのノッチに見出しが食い込んでいたり、画面下のホームインジケーターに大事なボタンが隠れていたり…。Web開発に慣れていると、こういう物理的な「侵食領域」って意識しないので、初見だとびっくりします。

結論から言うと、これは React Native の SafeAreaView というコンポーネントで一発解決できます。

  • SafeAreaView=ノッチ・ステータスバー・ホームインジケーターを避けてコンテンツを配置するコンポーネント
  • 使い方は <View><SafeAreaView> に置き換えるだけ
  • ただし、コアの SafeAreaView には罠があるので、現代では react-native-safe-area-context が推奨
  • Expoには標準で入っているので、最初から正しい方を使える

本記事では、「画面が見切れる/被る」問題に出会った React Native 初心者〜中級者向けに、セーフエリアの考え方と SafeAreaView の正しい使い方を解説します。実装パターン3選とよくあるハマりどころも網羅しているので、これ1本で安全領域の扱いがマスターできます。

そもそも「セーフエリア」って何?

SafeAreaView の話に入る前に、まず「セーフエリア(Safe Area)」という概念を整理しておきましょう。

セーフエリアとは、「コンテンツを配置しても安全な、画面上の領域」のこと。逆に言えば、その外側は「物理的・OS的に何かが被ってしまう領域」です。

スマホの「危険領域」たち

┌─────────────────────────────┐
│ ▓▓▓ ノッチ/Dynamic Island ▓▓▓ │ ← ここに文字を置くと隠れる
├─────────────────────────────┤
│ ステータスバー(時計・通信状態)   │ ← OSが描画する領域
├─────────────────────────────┤
│                             │
│                             │
│         セーフエリア          │ ← ここに置けば安全
│         (安全領域)          │
│                             │
│                             │
├─────────────────────────────┤
│ ▓▓▓ ホームインジケーター ▓▓▓   │ ← Androidのナビゲーションバーも
└─────────────────────────────┘

具体例:iOSとAndroidそれぞれの危険領域

  • iOS(iPhone X以降):上部のノッチ/Dynamic Island、下部のホームインジケーター
  • iOS(旧機種):上部のステータスバーのみ
  • Android:上部のステータスバー、下部のナビゲーションバー(機種・OS設定で異なる)
  • iPad / 大画面端末:基本的にはステータスバー、ホームインジケーター(あれば)

厄介なのは、これらの領域が機種ごと・OSごとに異なること。「iPhone 14 Proでは大丈夫だったのに、SEだと崩れる」「Androidの機種によって挙動が違う」みたいな事故が起きやすい。だからこそ、OSが動的に教えてくれるセーフエリア情報を使ってコンテンツを配置する必要があるんです。

SafeAreaViewの基本

React Native でセーフエリアを扱うための基本コンポーネントが SafeAreaView。使い方はシンプルで、画面のルート要素に置くだけです。

何もしない場合(NG例)

import { View, Text, StyleSheet } from 'react-native'

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>こんにちは!</Text>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' },
  title: { fontSize: 24, padding: 20 },
})

これだと、「こんにちは!」がノッチやステータスバーに被って表示されることがあります。Webだったら気にしないところだけど、モバイルでは事故です。

SafeAreaViewで囲む(OK例)

import { SafeAreaView, Text, StyleSheet } from 'react-native'

export default function App() {
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>こんにちは!</Text>
    </SafeAreaView>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' },
  title: { fontSize: 24, padding: 20 },
})

これで、自動的にステータスバー/ノッチ/ホームインジケーターを避けてコンテンツが配置されます。たった2行(importと囲み)の変更だけ。Reactコンポーネントの基本を押さえている人なら、すぐ使えます(Reactコンポーネントの基本については React 19の新機能解説 も参考に)。

コアの<SafeAreaView>には罠がある

「これだけで終わりなら記事も短くて済むのに」と思った方、実はここから本題です。

React Native コアに含まれている SafeAreaView には、実は致命的な制約があります。それは…

  • iOSでしか効かない:Androidでは無視される
  • 動的なノッチ変化に対応していない:画面回転や折りたたみで挙動がおかしくなる
  • 細かい制御ができない:「上だけセーフエリア、下は無視」みたいなことが難しい

そして React Native の公式ドキュメントでも、react-native-safe-area-context の利用を推奨します」と明記されています。つまり、コアの SafeAreaView は実質「過去の遺物」になっているんです。

現在の推奨:react-native-safe-area-context

現代の React Native 開発では、react-native-safe-area-context というサードパーティライブラリを使うのが事実上の標準です。

特徴とメリット

  • iOS / Android 両対応:機種・OSバージョンの差を吸収
  • 動的なセーフエリア変更に対応:画面回転や折りたたみでもズレない
  • useSafeAreaInsets フック:細かい数値を取得して柔軟にレイアウトできる
  • Expo SDK に標準同梱:個別インストール不要
  • React Navigation などメジャーライブラリと統合済み

セットアップ

Expoを使っている場合は、すでにインストールされているのでスキップでOK。素のReact Nativeなら以下でインストール。

# Expo(インストール不要、念のため確認用)
npx expo install react-native-safe-area-context

# 素のReact Native
npm install react-native-safe-area-context
cd ios && pod install # iOSの場合は追加で必要

SafeAreaProviderでアプリ全体を囲む

このライブラリでは、まず SafeAreaProvider でアプリ全体を囲むのが基本。最上位の App.tsx_layout.tsx でこうします。

// App.tsx
import { SafeAreaProvider } from 'react-native-safe-area-context'
import HomeScreen from './screens/HomeScreen'

export default function App() {
  return (
    <SafeAreaProvider>
      <HomeScreen />
    </SafeAreaProvider>
  )
}

これで配下のコンポーネントがセーフエリア情報にアクセスできるようになります。

画面ごとにSafeAreaViewで囲む

// screens/HomeScreen.tsx
import { Text, StyleSheet } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'

export default function HomeScreen() {
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>こんにちは!</Text>
    </SafeAreaView>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' },
  title: { fontSize: 24, padding: 20 },
})

importを react-native から react-native-safe-area-context に変えるだけ。これで iOS / Android どちらも適切に対応されます。

実装パターン3選

セーフエリアの扱い方には、用途別に3つのパターンがあります。場面に応じて使い分けましょう。

パターンA:全画面ラップ(基本)

一番シンプルで、ほとんどの画面はこれで足ります。edges プロパティで上下左右どこにセーフエリアを適用するか指定可能。

import { SafeAreaView } from 'react-native-safe-area-context'

// 全方向にセーフエリア(デフォルト)
<SafeAreaView style={{ flex: 1 }}>
  <Content />
</SafeAreaView>

// 上下のみ
<SafeAreaView style={{ flex: 1 }} edges={['top', 'bottom']}>
  <Content />
</SafeAreaView>

// 上だけ(下は背景色で塗りつぶしたい時など)
<SafeAreaView style={{ flex: 1 }} edges={['top']}>
  <Content />
</SafeAreaView>

パターンB:ヘッダー・フッターを別の色にする

「ヘッダー部分は青、コンテンツ部分は白」みたいに、セーフエリアの上下で背景色を変えたい場合。普通に SafeAreaView で囲むと全部同じ背景色になってしまうので、こういう時は外側に色付きのViewを置く工夫が必要です。

import { View, StyleSheet } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'

export default function HomeScreen() {
  return (
    // 外側で青背景を確保(ステータスバー領域も青になる)
    <View style={{ flex: 1, backgroundColor: '#1976d2' }}>
      // SafeAreaViewでセーフエリアの内側だけ色を変える
      <SafeAreaView style={styles.content}>
        <Text>こんにちは</Text>
      </SafeAreaView>
    </View>
  )
}

const styles = StyleSheet.create({
  content: { flex: 1, backgroundColor: '#fff' },
})

パターンC:useSafeAreaInsetsで自由レイアウト

「カスタムヘッダーの高さをノッチ分だけ増やしたい」「下部のフローティングボタンをホームインジケーターより上に置きたい」など、細かい数値で制御したい場合は useSafeAreaInsets フックを使います。

import { View, Text, StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

export default function HomeScreen() {
  const insets = useSafeAreaInsets()
  // insets = { top: 47, bottom: 34, left: 0, right: 0 }(iPhone 14 Proの例)

  return (
    <View style={{ flex: 1 }}>
      <View style={[styles.header, { paddingTop: insets.top + 16 }]}>
        <Text style={styles.headerText}>マイアプリ</Text>
      </View>

      <View style={styles.body}>
        <Text>コンテンツ</Text>
      </View>

      <View style={[styles.fab, { bottom: insets.bottom + 16 }]}>
        <Text>+</Text>
      </View>
    </View>
  )
}

const styles = StyleSheet.create({
  header: {
    backgroundColor: '#1976d2',
    paddingHorizontal: 16,
    paddingBottom: 16,
  },
  headerText: { color: '#fff', fontSize: 20 },
  body: { flex: 1, padding: 16 },
  fab: {
    position: 'absolute',
    right: 16,
    width: 56,
    height: 56,
    borderRadius: 28,
    backgroundColor: '#1976d2',
    alignItems: 'center',
    justifyContent: 'center',
  },
})

insets.topinsets.bottompaddingbottom などに足すことで、機種ごとに動的に正しい余白を確保できます。これがいわば、現代のRN開発の本命の使い方です。

よくある失敗とトラブルシュート

SafeAreaView絡みで、初学者がハマりがちなトラブルとその対処法を紹介します。

失敗1:SafeAreaProviderで囲み忘れ

useSafeAreaInsets を使っているのに、ルートで SafeAreaProvider を設定していない場合、 insets が常に { top: 0, bottom: 0, ... } になってしまいます。

対処App.tsx_layout.tsx など最上位で SafeAreaProvider でアプリ全体を囲んでいるか確認。React Navigation を使っている場合、Navigatorよりも外側に置く必要があります。

失敗2:間違ったライブラリの SafeAreaView を import している

// ❌ コアの SafeAreaView(古い、Android効かない)
import { SafeAreaView } from 'react-native'

// ✅ react-native-safe-area-context(推奨)
import { SafeAreaView } from 'react-native-safe-area-context'

意外とこのimport違いが原因で「SafeAreaView入れたのに効かない!」となるケース、めちゃくちゃ多いです。

失敗3:ScrollView と組み合わせて挙動が変

SafeAreaView の中に ScrollView を入れた時、スクロールの一番下まで行くとホームインジケーターに被って見える…という現象。これは SafeAreaView がスクロール内のコンテンツに対して効かないため。

対処ScrollViewcontentContainerStylepaddingBottom を設定するか、useSafeAreaInsets で取得した bottom を加える。

import { ScrollView } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

export default function ListScreen() {
  const insets = useSafeAreaInsets()

  return (
    <ScrollView
      contentContainerStyle={{
        padding: 16,
        paddingBottom: insets.bottom + 16, // 下部余白を確保
      }}
    >
      {/* リスト内容 */}
    </ScrollView>
  )
}

失敗4:モーダル表示時にセーフエリアが効かない

React Native の Modal コンポーネントを使うと、内部でセーフエリアが効かない場合があります。これはモーダルが別のビュー階層で表示されるため。

対処:モーダルの中身も SafeAreaView で囲む、または useSafeAreaInsets で個別対応。

失敗5:StatusBar の色が変わらない

「セーフエリアは効いてるのに、ステータスバーの文字が暗くて見えない」みたいな問題。これはセーフエリアとは別、StatusBar コンポーネントで明示的に指定する必要があります。

import { StatusBar } from 'expo-status-bar' // または 'react-native'

<StatusBar style="light" />  // 白文字
<StatusBar style="dark" />   // 黒文字(デフォルト)

Expoでの扱い

Expoは React Native の代表的な開発フレームワークで、react-native-safe-area-context が標準で同梱されているのが大きなメリット。初心者は迷わず Expo で始めるのが圧倒的にスムーズです。

Expoでの最小構成

// app/_layout.tsx(Expo Router使用時)
import { Stack } from 'expo-router'
import { SafeAreaProvider } from 'react-native-safe-area-context'

export default function RootLayout() {
  return (
    <SafeAreaProvider>
      <Stack />
    </SafeAreaProvider>
  )
}

// app/index.tsx(個別画面)
import { Text } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'

export default function HomeScreen() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <Text>こんにちは</Text>
    </SafeAreaView>
  )
}

Expo Router を使う場合、_layout.tsxSafeAreaProvider を設置するのが基本パターン。これで配下の全画面で SafeAreaViewuseSafeAreaInsets が使えるようになります。

iOS と Android の考え方の違い

同じ React Native コードでも、iOS と Android ではセーフエリアの考え方が微妙に違います。これを知っておくと、両OSで違和感ない UI を設計できます。

項目 iOS Android
上部の障害物 ノッチ/Dynamic Island/ステータスバー ステータスバー
下部の障害物 ホームインジケーター(最近の機種) ナビゲーションバー(機種・設定で変動)
セーフエリアの厳密さ 厳密(OSが正確な値を提供) 柔軟(edge-to-edgeなど自由度高い)
機種差の大きさ 世代で違う(旧iPhone vs Pro Max) 非常に大きい(メーカー・OS版で多様)

iOSは「正しいセーフエリア」がOSから提供される文化、Androidは「edge-to-edge(端から端まで)」が前提でより自由、というイメージ。react-native-safe-area-context はこれらの違いをいい感じに吸収してくれるので、基本は useSafeAreaInsets に任せておけば両OSで違和感のないUIになります。

まとめ:SafeAreaViewはモバイルUI設計の前提知識

本記事のポイントを整理します。

  • セーフエリア=ノッチ・ステータスバー・ホームインジケーターを避けた安全領域
  • コアの SafeAreaView(react-native)は使うな:iOS専用+古い、現代では非推奨
  • 現代の標準は react-native-safe-area-context:iOS/Android両対応、Expoに標準同梱
  • 使い方は3パターン:全画面ラップ/背景色分け/useSafeAreaInsetsでカスタム
  • ハマりどころ:SafeAreaProviderの囲み忘れ、importミス、ScrollView、Modal、StatusBar
  • iOSとAndroidの考え方の違いを理解すると両OSで違和感のないUI設計ができる

「ステータスバーに文字が被る」って初見だとびっくりするけど、SafeAreaViewのことを知っていれば、現代のスマホアプリの安全領域なんて怖くない。知ってるか知らないかだけの差です。

そして、もっと大事なのは「機種ごとに違う物理的な制約をOSが教えてくれて、フレームワークが吸収してくれる」というモバイル開発の文化を知ること。Webだけやってきた人がモバイルに来ると、こういう「物理的制約」の発想がなくて戸惑うんですが、慣れると逆に「Webって自由だな」と感じるはず。

次にReact Nativeで画面を作るときは、まず最初に SafeAreaView で囲む。これだけで、ユーザーの体験品質が一段階上がります。

React Native・モバイル開発をさらに深める関連記事

SafeAreaView でモバイルUIの基礎を押さえたら、React・フロントエンド全体の理解を深めて、Web/モバイル両方で戦えるエンジニアを目指しましょう。実装力が一段階上がる関連記事を厳選しました。

React・フロントエンドの基礎力を強化

API設計・バックエンドとの連携

設計力・用語の引き出しを増やす

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次