🐤

React Native Expo のプロジェクトに Tamagui を導入

2024/01/18に公開

Tamagui をお試し中です。Web と React Native の両方に対応しており、モノレポ構成等でコードを共通化できるのが強みの 1 つです。が、今回はシンプルに、React Native Expo 単体のプロジェクトに Tamagui を導入してみます。

事前準備

  • Node.js をインストールしておいてください。
  • お手元のスマートフォンに Expo Go をインストールしておいてください。

執筆時の環境

  • node v20.10.0
  • create-expo-app v2.1.1
  • expo v49.0.21
  • tamagui v1.82.3

React Native Expo のプロジェクトを作成

まず初めに、create-expo-app をインストール後、新規の expo プロジェクトを立ち上げましょう。ターミナルから以下のコマンドを実行してください。プロジェクト名は自由に設定していただいて OK です。

npm install --save-dev create-expo-app
npx create-expo-app my-expo-project --template

--template オプションを付けておくとプロジェクトのテンプレートを選択できます。

今回は Expo Router を使用したかったため、Navigation (TypeScript) - File-based routing with TypeScript enabled を選択しました。

プロジェクトの作成が完了すると、my-expo-project ディレクトリの配下にファイル・ディレクトリ群が生成されます。

以降は、my-expo-project ディレクトリ内に移動して作業していきます。

サーバの立ち上げ

ここで一度 expo サーバを立ち上げてみましょう。ターミナルから以下のコマンドを実行し、表示された QR コードをスマートフォンの Expo Go でスキャンしてください。

npm run start

無事サンプルアプリケーションが表示されれば OK です。

Tamagui の導入

Tamagui をインストールします。

npm install tamagui

併せて、設定を簡便にするため、提供されているテーマなどをインストールしておきます。追々カスタマイズしていくことになるかと思いますが、最初はシンプルにいきましょう。

npm install @tamagui/font-inter
npm install @tamagui/shorthands
npm install @tamagui/themes

続いて、プロジェクトのルートに tamagui.config.ts を作成します。中身は 公式ドキュメント の Quick Start を参考にしつつ、以下のようにしました。

import { createInterFont } from "@tamagui/font-inter";
import { shorthands } from '@tamagui/shorthands'
import { themes, tokens } from '@tamagui/themes'
import { createTamagui } from 'tamagui'

const headingFont = createInterFont();
const bodyFont = createInterFont();

const appConfig = createTamagui({
  themes,
  tokens,
  shorthands,
  fonts: {
    heading: headingFont,
    body: bodyFont,
  },
})

export type AppConfig = typeof appConfig

declare module 'tamagui' {
  interface TamaguiCustomConfig extends AppConfig {}
}

export default appConfig

続いて、_layout.tsx に Tamagui の Provider を追加します。今回はプロジェクト全体に適用させたいので、app 直下の _layout.tsx を以下のように変更します。

import FontAwesome from '@expo/vector-icons/FontAwesome';
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { SplashScreen, Stack } from 'expo-router';
import { useEffect } from 'react';
import { useColorScheme } from 'react-native';
import { TamaguiProvider } from "tamagui";  // インポートの追加
import appConfig from "../tamagui.config";  // インポートの追加

export {
  ErrorBoundary,
} from 'expo-router';

export const unstable_settings = {
  initialRouteName: '(tabs)',
};

SplashScreen.preventAutoHideAsync();

export default function RootLayout() {
  const [loaded, error] = useFonts({
    SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
    Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"),  // フォント追加
    InterBold: require("@tamagui/font-inter/otf/Inter-Bold.otf"),  // フォント追加
    ...FontAwesome.font,
  });

  useEffect(() => {
    if (error) throw error;
  }, [error]);

  useEffect(() => {
    if (loaded) {
      SplashScreen.hideAsync();
    }
  }, [loaded]);

  if (!loaded) {
    return null;
  }

  return <RootLayoutNav />;
}

function RootLayoutNav() {
  const colorScheme = useColorScheme();

  // TamaguiProvider で囲む (デフォルトテーマとしてダークテーマを指定)
  return (
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <TamaguiProvider config={appConfig} defaultTheme="dark">
        <Stack>
          <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
          <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
        </Stack>
      </TamaguiProvider>
    </ThemeProvider>
  );
}

Button を表示してみる

最後に、Tamagui の Button コンポーネント を利用してみましょう。

app/(tabs)/index.tsx を以下のように書き換えます。

import { Button, YStack } from 'tamagui';

export default function TabOneScreen() {
  return (
    <YStack padding="$10">
      <Button size="$6">Button</Button>
    </YStack>
  );
}

Expo Go で再度アプリを確認してみます。以下のようにボタンが表示されていれば成功です。

まとめ

本記事では、React Native Expo のプロジェクトに Tamagui を導入する手順を紹介しました。
最後に、本記事で作成したプロジェクトのリポジトリを貼っておきます。

https://github.com/matsu3m/react-native-expo-tamagui-for-zenn

ここまでお読みいただきありがとうございました!

Discussion