💎

React NativeでLiquid Glassに対応する

に公開

こんにちは!テラーノベルでフロントエンドを担当している@kazutoyoです!

こちらはReact Native Advent Calendar 2025の6日目の投稿です。

https://qiita.com/advent-calendar/2025/react-native

昨日の@JunichiHashimotoさんの「Gemini3を活用して個人アプリをExpo53からExpo54に移行してLiquid glassに対応してみた!」に続き、React Native(Expo)でLiquid Glassを適用する方法について紹介します。

Liquid Glassについて

2025年、Appleは次期iOS 26で「Liquid Glass」という新しいデザイン言語を導入しました。Liquid Glassは流体と光の屈折という現実の自然界をモチーフとし、コンテンツへの集中を高め、様々なデバイス・プラットフォーム間で統一したUIを実現することを目的としています。

https://www.apple.com/jp/newsroom/2025/06/apple-introduces-a-delightful-and-elegant-new-software-design/

今回の記事では、React Nativeを使ったアプリ開発において、このLiquid Glassにどのように対応できるのか、実際のコード例を交えながら解説しようと思います。

Liquid Glassとは?その目的

Liquid Glassは、Appleが提唱する新しいデザインパラダイムです。主な目的は以下の3点です

  1. コンテンツへの集中を高める:視覚的なノイズを減らし、ユーザーが本当に見たいコンテンツに集中できるようにする
  2. 統一されたUI体験:様々なデバイスやプラットフォーム間で一貫性のあるUIを提供する
  3. 直感的なデザイン:流体と光の屈折という自然界のメタファーを使い、視覚的に分かりやすく直感的なインターフェースを実現する

ただし、重要な注意点として、過度にLiquid Glassの表現を取り入れてコンテンツに集中できないようにすることは本末転倒です。あくまでコンテンツファーストの考え方を忘れないようにしましょう。

https://developer.apple.com/jp/videos/play/wwdc2025/219/

https://developer.apple.com/jp/videos/play/wwdc2025/356/

React NativeでLiquid Glassに対応する方法

React NativeはiOS/AndroidのネイティブのUIを表示することができます。
そのため、仕組み上はReact NativeでもLiquid Glassに対応できます。

ただし、ネイティブの機能をラップしている関係上、まだReact Nativeで対応できていない部分もいくつかあるので注意が必要です。

サンプルアプリについて

今回の記事では、Liquid Glassに対応したTodoアプリを実装し、GitHubで公開しています。
以下の実装例は、このサンプルアプリのコードを元に解説しています。

https://github.com/kazutoyo/liquid-glass-todo-sample

実際に動作するコードを確認したい方は、ぜひリポジトリをご覧ください。

動作環境

サンプルアプリで使用しているライブラリのバージョンは以下の通りです

  • Expo SDK: 54.0.24
  • Expo Router: 6.0.15
  • React Native: 0.81.5
  • React Native Screens: 4.18.0
  • expo-glass-effect: 0.1.7
  • @expo/ui: 0.2.0-beta.7 (ベータ版)
  • React: 19.1.0

次から、実際に各要素をどのように対応するか見ていきましょう。

タブバーの対応

Expo Router v6では、Native Tabsが新たにサポートされました(現時点ではExperimentalステータス)。

https://docs.expo.dev/router/advanced/native-tabs/

このNative Tabsを利用することで、Liquid Glassに対応したタブバーを簡単に実装できます。

基本的な実装方法

expo-router/unstable-native-tabsをインポートして、以下のように実装します

app/_layout.tsx
import { NativeTabs, Icon, Label } from 'expo-router/unstable-native-tabs';

export default function TabLayout() {
  return (
    <NativeTabs>
      <NativeTabs.Trigger name="index">
        <Label>Home</Label>
        <Icon sf="house.fill" drawable="custom_android_drawable" />
      </NativeTabs.Trigger>
      <NativeTabs.Trigger name="settings">
        <Icon sf="gear" drawable="custom_settings_drawable" />
        <Label>Settings</Label>
      </NativeTabs.Trigger>
    </NativeTabs>
  );
}

この実装により、iOSではSF Symbolsを使ったアイコン、Androidではカスタムdrawableを使ったネイティブなタブバーが表示されます。

バッジの表示

タブにバッジを表示したい場合は、Badgeコンポーネントを追加するだけです

app/_layout.tsx
import { Badge, Icon, Label, NativeTabs } from 'expo-router/unstable-native-tabs';

export default function TabLayout() {
  return (
    <NativeTabs>
      <NativeTabs.Trigger name="home">
        <Label>Home</Label>
        <Icon sf="house.fill" drawable="custom_android_drawable" />
      </NativeTabs.Trigger>
      <NativeTabs.Trigger name="settings">
        <Icon sf="gear" drawable="custom_settings_drawable" />
        <Label>Settings</Label>
        <Badge>9+</Badge>
      </NativeTabs.Trigger>
    </NativeTabs>
  );
}

検索タブの独立表示

iOS 26では、検索タブを独立したタブとして表示できる機能が追加されました。

Expo Routerの <NativeTabs.Trigger>role="search"を設定することで、検索タブが独立して表示され、より直感的なUI体験を提供できます

app/_layout.tsx
import { NativeTabs, Label } from 'expo-router/unstable-native-tabs';

export default function TabLayout() {
  return (
    <NativeTabs>
      <NativeTabs.Trigger name="index">
        <Label>Home</Label>
      </NativeTabs.Trigger>
      <NativeTabs.Trigger name="search" role="search">
        <Label>Search</Label>
      </NativeTabs.Trigger>
    </NativeTabs>
  );
}

Bottom Accessory

ミュージックアプリのような、タブの上にフローティングし、スクロールしたときに最小化してタブの右側に表示されるViewは「Bottom Accessory」と呼ばれます。

残念ながら、執筆時点ではExpo RouterではまだこのBottom Accessory機能に対応していません。ただし、React Native Screensでは対応するPRがすでにマージされているため、次期Expo SDK 55では対応される可能性が高いです。

参考:React Native Screens PR #3288

また、React Native Bottom Tabsを利用することで、Bottom Accessoryに対応することができます。

React Native Bottom TabsはExpo Routerとの連携もできるため、現時点でBottom Accessoryを使う場合はこちらを利用するのも良いかもしれません。
https://x.com/o_kwasniewski/status/1979505300643446857?s=20

https://github.com/callstackincubator/react-native-bottom-tabs

iOS 26のナビゲーションバーでは、右側のボタンがLiquid Glassのスタイルで表示されるようになりました。

この機能を使うには、Expo Router v7(現在はCanary版)を利用する、もしくはExpo Router v6にパッチを当てる必要があります。
(これは内部的に利用しているReact Native Screensの6.18.0以上が必要なためです。)

unstable_headerRightItemsを使うことで、ネイティブなヘッダーボタンを配置でき、iOS 26ではLiquid Glassスタイルで表示されます。variantプロパティで、ボタンのスタイル(plaindone)を指定できます。

https://reactnavigation.org/docs/native-stack-navigator/#header-items

コード
app/screen.tsx
export default function Screen() {
  return (
    <Stack.Screen
      options={{
        title: "My Screen",
        unstable_headerRightItems: () => [
          {
            type: 'menu',
            label: 'Options',
            icon: {
              type: 'sfSymbol',
              name: 'ellipsis',
            },
            menu: {
              title: 'Options',
              items: [
                {
                  type: 'action',
                  label: 'Edit',
                  icon: {
                    type: 'sfSymbol',
                    name: 'pencil',
                  },
                  onPress: () => {
                    // Do something
                  },
                },
                {
                  type: 'submenu',
                  label: 'More',
                  items: [
                    {
                      type: 'action',
                      label: 'Delete',
                      destructive: true,
                      onPress: () => {
                        // Do something
                      },
                    },
                  ],
                },
              ],
            },
          },
        ],
      }}
    >
      {/* Your screen content */}
    </Stack.Screen>
  );
}

Expo UIを使ったPrimitive UI

@expo/uiパッケージを使用することで、SwiftUIベースのネイティブUIコンポーネントを実装できます。

https://docs.expo.dev/versions/latest/sdk/ui/

例えば、コンテキストメニューは以下のように実装します。

components/ContextMenuExample.tsx
import { ContextMenu, Host, Button } from '@expo/ui/swift-ui';

export default function ContextMenuExample() {
  return (
    <Host style={{ width: 150, height: 50 }}>
      <ContextMenu>
        <ContextMenu.Items>
          <Button
            systemImage="person.crop.circle.badge.xmark"
            onPress={() => console.log('Pressed1')}>
            Hello
          </Button>
        </ContextMenu.Items>
        <ContextMenu.Trigger>
          <Button variant="bordered">
            Show Menu
          </Button>
        </ContextMenu.Trigger>
      </ContextMenu>
    </Host>
  );
}

@expo/uiを使うことで、iOSのネイティブコンポーネントをReact Nativeから直接利用でき、Liquid Glassの恩恵を受けることができます。

カスタムViewでのLiquid Glass効果

expo-glass-effectパッケージを使用することで、カスタムViewにLiquid Glassのガラス効果を適用できます。

https://docs.expo.dev/versions/latest/sdk/glass-effect/

components/GlassExample.tsx
import { GlassView, GlassContainer } from 'expo-glass-effect';
import { StyleSheet } from 'react-native';

export const GlassContainerDemo = () => {
  return (
    <GlassContainer spacing={10} style={styles.containerStyle}>
      <GlassView style={styles.glass1} isInteractive />
      <GlassView style={styles.glass2} />
      <GlassView style={styles.glass3} />
    </GlassContainer>
  );
};

const styles = StyleSheet.create({
  containerStyle: {
    flex: 1,
    padding: 20,
    gap: 10,
  },
  glass1: {
    height: 100,
    borderRadius: 12,
  },
  glass2: {
    height: 100,
    borderRadius: 12,
  },
  glass3: {
    height: 100,
    borderRadius: 12,
  },
});

GlassContainerGlassViewの親Viewとして使用し、spacingプロパティでGlassView同士が接するときの融合効果を設定できます。

React NativeでのLiquid Glass対応について

実際にReact NativeでLiquid Glassに対応してみて、以下のようなメリットと課題が見えてきました。

メリット

ネイティブなUI体験の実現

  • Expo UIなどを活用することで、SwiftUIの機能をReact Nativeでも利用できる
  • iOS標準のUIパターンを簡単に実装できる

既存のExpo環境との親和性

  • Expo Routerを使っている場合、比較的少ない変更で対応可能
  • 段階的な導入がしやすい

課題と考慮点

1. APIの安定性

多くの機能がunstable_プレフィックス付きで提供されており、APIがまだ安定していません。将来的に破壊的変更が入る可能性があるため、使用には注意が必要です。

2. 機能の不足

Bottom Accessoryなど、iOS 26で利用可能な一部の機能がまだExpo Routerで対応されていません。ただし、内部的に利用しているReact Native Screensでは既に実装されているため、今後のアップデートで対応される見込みです。

3. 既存デザインとの整合性

既存のアプリにLiquid Glassを適用する場合、既存のデザインシステムとの調和を慎重に検討する必要があります

  • Bottom Tabsを採用できるか
  • コンポーネントの角丸をiOS向けに調整するか
  • ボタンやカードのスタイルをどこまでLiquid Glassに寄せるか

4. プラットフォーム間の分岐

iOSとAndroidで実装を分ける必要が出てきます。特にExpo UIを使う場合は、完全に異なる実装が必要になることもあります。また、iOS 26以上でしか使えない機能を、それ以外の環境でどうフォールバックするかも考える必要があります。

実装の判断基準

Liquid Glassへの対応を検討する際は、以下のステップで進めることをおすすめします

  1. メリットの洗い出し:Liquid Glassに対応することで、ユーザー体験がどう向上するか具体的に検討する
  2. 対応範囲の決定:すべてに対応するのではなく、効果の高い部分から段階的に対応する
  3. フォールバック戦略:iOS 26未満やAndroidでどう表示するかを事前に決めておく
  4. メンテナンスコスト:分岐が増えることによるコード複雑性の増加を許容できるか検討する

まとめ

React Nativeでも、Expo Routerや各種ライブラリを活用することで、iOS 26のLiquid Glassに対応することができます。

ただし、現時点ではまだ一部未対応の機能があり、APIも安定していない部分があるため、慎重に対応範囲を検討する必要があります。

細かな調整をするほどプラットフォーム間の分岐も増えるため、どの程度対応していくかは、アプリの特性やターゲットユーザー、開発リソースを考慮して判断しましょう。

まずはLiquid Glassに対応することでどのようなメリットがあるかを十分に検討し、何を対応すべきか優先順位をつけてから段階的に実装していくのが良いアプローチだと思います。

それでは、よいReact Nativeライフを!

参考文献


こちらの記事は、React Native Meetup #23での発表を元に作成しています。

https://rnm-23.kazutoyo.jp/

テラーノベル テックブログ

Discussion