├── .gitignore ├── .npmrc ├── .prettierignore ├── .svgrrc ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── app-env.d.ts ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── icon.png └── splash.png ├── babel.config.js ├── eas.json ├── eslint.config.js ├── git-hooks └── pre-commit ├── global.css ├── metro.config.cjs ├── nativewind-env.d.ts ├── package.json ├── patches └── metro.patch ├── pnpm-lock.yaml ├── prettier.config.js ├── src ├── app │ ├── (app) │ │ ├── (tabs) │ │ │ ├── _layout.tsx │ │ │ ├── index.tsx │ │ │ └── two.tsx │ │ └── _layout.tsx │ ├── +html.tsx │ ├── +not-found.tsx │ ├── _layout.tsx │ └── login.tsx ├── i18n │ └── getLocale.tsx ├── lib │ └── cx.tsx ├── setup.tsx ├── tests │ └── App.test.tsx ├── ui │ ├── BottomSheetModal.tsx │ ├── Text.tsx │ └── colors.ts └── user │ └── useViewerContext.tsx ├── tailwind.config.ts ├── translations └── ja_JP.json ├── tsconfig.json └── vitest.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env.local 3 | .eslintcache 4 | .expo/ 5 | .metro-health-check* 6 | *.jks 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | *.p12 11 | *.p8 12 | *.swp 13 | android 14 | credentials.json 15 | dist/ 16 | expo-env.d.ts 17 | ios 18 | node_modules/ 19 | npm-debug.* 20 | tsconfig.tsbuildinfo 21 | web-build/ 22 | 23 | # fbtee 24 | .enum_manifest.json 25 | .src_manifest.json 26 | source_strings.json 27 | src/translations/ 28 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | resolution-mode=highest 2 | node-linker=hoisted 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | __generated__ 2 | .enum_manifest.json 3 | .expo/ 4 | .source_strings.json 5 | .src_manifest.json 6 | android 7 | coverage 8 | dist/ 9 | ios 10 | patches/ 11 | pnpm-lock.yaml 12 | web-build/ 13 | -------------------------------------------------------------------------------- /.svgrrc: -------------------------------------------------------------------------------- 1 | { 2 | "replaceAttrValues": { 3 | "currentColor": "{props.currentColor}" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "styled-components.vscode-styled-components", 6 | "sysoev.vscode-open-in-github", 7 | "usernamehw.errorlens", 8 | "wix.vscode-import-cost" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "files.insertFinalNewline": true, 4 | "files.trimFinalNewlines": true, 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": "explicit" 7 | }, 8 | "[javascript]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | }, 11 | "[typescriptreact]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode" 13 | }, 14 | "[typescript]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "[json]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "typescript.preferences.importModuleSpecifierEnding": "js", 21 | "typescript.reportStyleChecksAsWarnings": false, 22 | "typescript.updateImportsOnFileMove.enabled": "always", 23 | "typescript.tsdk": "node_modules/typescript/lib", 24 | "search.exclude": { 25 | "android/app/build/**": true, 26 | "ios/DerivedData/**": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The React Native & Expo App Template 2 | 3 | This is the most modern and always up-to-date React Native & Expo app template. It comes with sensible defaults, a great developer experience and is optimized for performance. You can read more about the DevX setup in this [frontend tooling article](https://cpojer.net/posts/fastest-frontend-tooling-in-2022). Check out the corresponding [web app template](https://github.com/nkzw-tech/vite-ts-react-tailwind-template). 4 | 5 | 6 | 7 | 8 | ## Technologies 9 | 10 | You have to make a lot of decisions and install tons of packages every time you create a new React Native app. This template offers an opinionated starting point and includes the best options for various categories. Instead of spending hours on research and piecing together a setup that works, you can just copy this template and start right away. When you copy this template, you get full control to add or remove any third-party package to customize your app. 11 | 12 | - Expo 53 & React Native 0.79 with the New Architecture. 13 | - [Expo Router](https://docs.expo.dev/router/introduction/) 14 | - [NativeWind](https://www.nativewind.dev/) & [Tailwind CSS](https://tailwindcss.com/) 15 | - [`@gorhom/bottom-sheet`](https://github.com/gorhom/react-native-bottom-sheet), [Legend List](https://github.com/LegendApp/legend-list), [`react-native-svg`](https://github.com/software-mansion/react-native-svg) (+ `react-native-svg-transformer`), [`expo-linear-gradient`](https://docs.expo.dev/versions/latest/sdk/linear-gradient/). 16 | - [`fbtee`](https://github.com/nkzw-tech/fbtee) for i18n. 17 | - [TypeScript](https://www.typescriptlang.org) 18 | - [React Compiler](https://react.dev/learn/react-compiler) 19 | - [pnpm](https://pnpm.io/) 20 | - **ESM:** _It's 2025._ This template comes with `"type": "module"`. 21 | 22 | ## Getting Started 23 | 24 | Start here: [Create a new app using this template](https://github.com/new?template_name=expo-app-template&template_owner=nkzw-tech). 25 | 26 | After you created your repo, you can freely modify anything in this template. 27 | 28 | ### Prerequisites 29 | 30 | You'll need Node.js 22, pnpm 10+ and Cocoapods. 31 | 32 | ```bash 33 | 34 | brew install node pnpm cocoapods 35 | ``` 36 | 37 | For building and running apps locally, follow the [Expo setup guides](https://docs.expo.dev/get-started/set-up-your-environment/?platform=ios&device=simulated). 38 | 39 | ### Installing Dependencies 40 | 41 | Run: 42 | 43 | ```bash 44 | pnpm install && pnpm dev:setup 45 | ``` 46 | 47 | ### Running the iOS App in a simulator 48 | 49 | ```bash 50 | pnpm prebuild 51 | pnpm ios 52 | ``` 53 | 54 | If you already have the app installed on your simulator, you can skip the above steps and simply run `pnpm dev` to start the development server. 55 | 56 | ## Contributing 57 | 58 | Feel free to open issues, initiate discussions and send PRs to improve the template. 59 | -------------------------------------------------------------------------------- /app-env.d.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | /// 3 | /// 4 | 5 | declare module '*.svg' { 6 | import { FC } from 'react'; 7 | import { SvgProps } from 'react-native-svg'; 8 | 9 | const content: FC; 10 | export default content; 11 | } 12 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "NKZW App", 4 | "slug": "nkzw-app", 5 | "version": "1.0.0", 6 | "scheme": "nkzw-app", 7 | "web": { 8 | "bundler": "metro", 9 | "output": "static", 10 | "favicon": "./assets/favicon.png" 11 | }, 12 | "plugins": [ 13 | "expo-router", 14 | [ 15 | "expo-dev-launcher", 16 | { 17 | "launchMode": "most-recent" 18 | } 19 | ], 20 | "expo-localization", 21 | "expo-font", 22 | "expo-web-browser", 23 | "react-native-edge-to-edge" 24 | ], 25 | "experiments": { 26 | "reactCanary": true, 27 | "reactCompiler": true, 28 | "buildCacheProvider": { 29 | "plugin": "expo-build-disk-cache" 30 | }, 31 | "tsconfigPaths": true, 32 | "typedRoutes": true 33 | }, 34 | "orientation": "portrait", 35 | "icon": "./assets/icon.png", 36 | "userInterfaceStyle": "light", 37 | "splash": { 38 | "image": "./assets/splash.png", 39 | "resizeMode": "contain", 40 | "backgroundColor": "#ffffff" 41 | }, 42 | "assetBundlePatterns": ["**/*"], 43 | "ios": { 44 | "supportsTablet": true, 45 | "bundleIdentifier": "app.nkzw.www", 46 | "infoPlist": { 47 | "ITSAppUsesNonExemptEncryption": false 48 | } 49 | }, 50 | "android": { 51 | "adaptiveIcon": { 52 | "foregroundImage": "./assets/adaptive-icon.png", 53 | "backgroundColor": "#ffffff" 54 | }, 55 | "package": "app.nkzw.www" 56 | }, 57 | "newArchEnabled": true, 58 | "extra": { 59 | "router": { 60 | "origin": false 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkzw-tech/expo-app-template/650343429d93d95e41cc6d0149e53834d19c1b82/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkzw-tech/expo-app-template/650343429d93d95e41cc6d0149e53834d19c1b82/assets/favicon.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkzw-tech/expo-app-template/650343429d93d95e41cc6d0149e53834d19c1b82/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkzw-tech/expo-app-template/650343429d93d95e41cc6d0149e53834d19c1b82/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | export default function (api) { 2 | api.cache(true); 3 | 4 | return { 5 | presets: [ 6 | '@nkzw/babel-preset-fbtee', 7 | ['babel-preset-expo', { jsxImportSource: 'nativewind' }], 8 | 'nativewind/babel', 9 | ], 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 16.1.0", 4 | "appVersionSource": "remote" 5 | }, 6 | "build": { 7 | "development": { 8 | "developmentClient": true, 9 | "distribution": "internal" 10 | }, 11 | "preview": { 12 | "distribution": "internal" 13 | }, 14 | "production": { 15 | "autoIncrement": true 16 | } 17 | }, 18 | "submit": { 19 | "production": {} 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import nkzw from '@nkzw/eslint-config'; 2 | import fbtee from '@nkzw/eslint-plugin-fbtee'; 3 | 4 | export default [ 5 | ...nkzw, 6 | fbtee.configs.strict, 7 | { 8 | ignores: [ 9 | '__generated__', 10 | '.expo', 11 | 'android/', 12 | 'dist/', 13 | 'ios/', 14 | 'vite.config.ts.timestamp-*', 15 | ], 16 | }, 17 | { 18 | files: ['scripts/**/*.tsx'], 19 | rules: { 20 | 'no-console': 0, 21 | }, 22 | }, 23 | { 24 | files: ['metro.config.cjs'], 25 | rules: { 26 | '@typescript-eslint/no-require-imports': 0, 27 | }, 28 | }, 29 | { 30 | plugins: { 31 | '@nkzw/fbtee': fbtee, 32 | }, 33 | rules: { 34 | '@nkzw/fbtee/no-untranslated-strings': 0, 35 | '@typescript-eslint/array-type': [2, { default: 'generic' }], 36 | '@typescript-eslint/no-restricted-imports': [ 37 | 2, 38 | { 39 | paths: [ 40 | { 41 | importNames: ['Text'], 42 | message: 43 | 'Please use the corresponding UI components from `src/ui/` instead.', 44 | name: 'react-native', 45 | }, 46 | { 47 | importNames: ['ScrollView'], 48 | message: 49 | 'Please use the corresponding UI component from `react-native-gesture-handler` instead.', 50 | name: 'react-native', 51 | }, 52 | { 53 | importNames: ['BottomSheetModal'], 54 | message: 55 | 'Please use the corresponding UI components from `src/ui/` instead.', 56 | name: '@gorhom/bottom-sheet', 57 | }, 58 | ], 59 | }, 60 | ], 61 | 'import-x/no-extraneous-dependencies': [ 62 | 2, 63 | { 64 | devDependencies: [ 65 | './eslint.config.js', 66 | './scripts/**.tsx', 67 | './tailwind.config.ts', 68 | './vitest.config.js', 69 | '**/*.test.tsx', 70 | ], 71 | }, 72 | ], 73 | }, 74 | settings: { 75 | 'import-x/resolver': { 76 | typescript: { 77 | project: './tsconfig.json', 78 | }, 79 | }, 80 | }, 81 | }, 82 | ]; 83 | -------------------------------------------------------------------------------- /git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g') 3 | [ -z "$FILES" ] && exit 0 4 | echo "$FILES" | xargs pnpm prettier --ignore-unknown --write 5 | echo "$FILES" | xargs git add 6 | 7 | exit 0 8 | -------------------------------------------------------------------------------- /global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /metro.config.cjs: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require('expo/metro-config'); 2 | const { withNativeWind } = require('nativewind/metro'); 3 | 4 | const config = getDefaultConfig(__dirname); 5 | 6 | module.exports = withNativeWind( 7 | { 8 | ...config, 9 | resolver: { 10 | ...config.resolver, 11 | assetExts: config.resolver.assetExts.filter((ext) => ext !== 'svg'), 12 | sourceExts: [...config.resolver.sourceExts, 'svg'], 13 | }, 14 | transformer: { 15 | ...config.transformer, 16 | babelTransformerPath: require.resolve( 17 | 'react-native-svg-transformer/expo', 18 | ), 19 | }, 20 | }, 21 | { 22 | input: './global.css', 23 | }, 24 | ); 25 | -------------------------------------------------------------------------------- /nativewind-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind. 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nkzw-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "main": "expo-router/entry", 7 | "scripts": { 8 | "android": "expo run:android", 9 | "dev": "expo start", 10 | "dev:setup": "pnpm fbtee", 11 | "fbtee": "pnpm run fbtee:manifest && pnpm run fbtee:collect && pnpm run fbtee:translate", 12 | "fbtee:collect": "fbtee collect --manifest < .src_manifest.json > source_strings.json", 13 | "fbtee:manifest": "fbtee manifest --src src", 14 | "fbtee:translate": "fbtee translate --source-strings source_strings.json --translations translations/*.json --jenkins --output-dir src/translations/", 15 | "format": "prettier --write .", 16 | "format-graphql": "./scripts/format-graphql-schema.tsx", 17 | "preinstall": "command -v git >/dev/null 2>&1 && git config core.hooksPath git-hooks || exit 0", 18 | "ios": "expo run:ios --device 'iPhone 16 Pro'", 19 | "lint": "eslint --cache .", 20 | "lint:format": "prettier --cache --check .", 21 | "prebuild": "expo prebuild", 22 | "start": "expo start --dev-client", 23 | "test": "NODE_OPTIONS='--no-experimental-detect-module' npm-run-all --parallel tsc:check vitest:run lint lint:format", 24 | "tsc:check": "tsc", 25 | "vitest:run": "vitest run", 26 | "web": "expo start --web" 27 | }, 28 | "eslintConfig": { 29 | "extends": "universe/native", 30 | "root": true 31 | }, 32 | "dependencies": { 33 | "@expo/vector-icons": "^14.1.0", 34 | "@gorhom/bottom-sheet": "^5.1.4", 35 | "@legendapp/list": "^1.0.14", 36 | "@nkzw/core": "^1.2.1", 37 | "@nkzw/create-context-hook": "^1.1.0", 38 | "@react-native-async-storage/async-storage": "^2.1.2", 39 | "@react-navigation/native": "^7.1.9", 40 | "@react-navigation/stack": "^7.3.2", 41 | "babel-plugin-react-compiler": "19.1.0-rc.2", 42 | "classnames": "^2.5.1", 43 | "expo": "53.0.9", 44 | "expo-constants": "~17.1.6", 45 | "expo-dev-client": "~5.1.8", 46 | "expo-font": "^13.3.1", 47 | "expo-linear-gradient": "^14.1.4", 48 | "expo-linking": "~7.1.5", 49 | "expo-localization": "^16.1.5", 50 | "expo-router": "5.0.7", 51 | "expo-system-ui": "~5.0.7", 52 | "expo-web-browser": "~14.1.6", 53 | "fbtee": "^0.2.2", 54 | "nativewind": "^4.1.23", 55 | "react": "^19.1.0", 56 | "react-dom": "^19.1.0", 57 | "react-native": "~0.79.2", 58 | "react-native-edge-to-edge": "^1.6.0", 59 | "react-native-gesture-handler": "^2.25.0", 60 | "react-native-reanimated": "^3.17.5", 61 | "react-native-safe-area-context": "^5.4.1", 62 | "react-native-screens": "4.11.0-beta.2", 63 | "react-native-svg": "^15.12.0", 64 | "react-native-web": "~0.20.0" 65 | }, 66 | "devDependencies": { 67 | "@ianvs/prettier-plugin-sort-imports": "^4.4.1", 68 | "@nkzw/babel-preset-fbtee": "^0.2.2", 69 | "@nkzw/eslint-config": "^3.0.0", 70 | "@nkzw/eslint-plugin-fbtee": "^0.2.2", 71 | "@react-native/metro-babel-transformer": "^0.79.2", 72 | "@types/react": "~19.1.5", 73 | "@vitejs/plugin-react": "^4.5.0", 74 | "eslint": "^9.27.0", 75 | "expo-build-disk-cache": "^0.4.4", 76 | "npm-run-all2": "^8.0.4", 77 | "prettier": "4.0.0-alpha.12", 78 | "prettier-plugin-packagejson": "^2.5.14", 79 | "prettier-plugin-tailwindcss": "^0.6.11", 80 | "react-native-svg-transformer": "^1.5.1", 81 | "tailwindcss": "^3.4.0", 82 | "typescript": "^5.8.3", 83 | "vitest": "^3.1.4", 84 | "vitest-react-native": "^0.1.5" 85 | }, 86 | "pnpm": { 87 | "updateConfig": { 88 | "ignoreDependencies": [ 89 | "tailwindcss" 90 | ] 91 | }, 92 | "patchedDependencies": { 93 | "metro": "patches/metro.patch" 94 | }, 95 | "ignorePatchFailures": false 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /patches/metro.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/lib/logToConsole.js b/src/lib/logToConsole.js 2 | index 5a51d4ba0bb6cc26cc880f666c29b015acd6eeee..28c67b88624b0fed01338b172416a78dde023778 100644 3 | --- a/src/lib/logToConsole.js 4 | +++ b/src/lib/logToConsole.js 5 | @@ -39,10 +39,8 @@ module.exports = (terminal, level, mode, ...data) => { 6 | if (typeof lastItem === "string") { 7 | data[data.length - 1] = lastItem.trimEnd(); 8 | } 9 | - const modePrefix = 10 | - !mode || mode == "BRIDGE" ? "" : `(${mode.toUpperCase()}) `; 11 | terminal.log( 12 | - color.bold(` ${modePrefix}${logFunction.toUpperCase()} `) + 13 | + color.bold(` ${logFunction.toUpperCase()} `) + 14 | "".padEnd(groupStack.length * 2, " "), 15 | util.format(...data) 16 | ); 17 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | importOrderParserPlugins: ['importAssertions', 'typescript', 'jsx'], 3 | plugins: [ 4 | '@ianvs/prettier-plugin-sort-imports', 5 | 'prettier-plugin-packagejson', 6 | // The order of plugins matters, and Tailwind CSS must be the last one. 7 | 'prettier-plugin-tailwindcss', 8 | ], 9 | singleQuote: true, 10 | tailwindAttributes: ['className'], 11 | tailwindFunctions: ['cx'], 12 | }; 13 | -------------------------------------------------------------------------------- /src/app/(app)/(tabs)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import _AntDesign from '@expo/vector-icons/AntDesign.js'; 2 | import { Tabs } from 'expo-router'; 3 | import { fbs } from 'fbtee'; 4 | import { FC } from 'react'; 5 | import { Pressable, View } from 'react-native'; 6 | import getLocale from 'src/i18n/getLocale.tsx'; 7 | import colors from 'src/ui/colors.ts'; 8 | import Text from 'src/ui/Text.tsx'; 9 | import useViewerContext from 'src/user/useViewerContext.tsx'; 10 | 11 | // Types in `@expo/vector-icons` do not currently work correctly in `"type": "module"` packages. 12 | const AntDesign = _AntDesign as unknown as FC<{ 13 | color: string; 14 | name: string; 15 | size: number; 16 | }>; 17 | 18 | export default function TabLayout() { 19 | const { locale, setLocale } = useViewerContext(); 20 | 21 | return ( 22 | 30 | ( 34 | setLocale(locale === 'ja_JP' ? 'en_US' : 'ja_JP')} 37 | > 38 | {({ pressed }) => ( 39 | 44 | {getLocale().split('_')[0]} 45 | 46 | )} 47 | 48 | ), 49 | tabBarIcon: ({ focused }: { focused: boolean }) => ( 50 | 55 | ), 56 | title: String(fbs('Home', 'Home tab title')), 57 | }} 58 | /> 59 | ( 63 | 68 | ), 69 | title: String(fbs('Two', 'Two tab title')), 70 | }} 71 | /> 72 | 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /src/app/(app)/(tabs)/index.tsx: -------------------------------------------------------------------------------- 1 | import { Stack } from 'expo-router'; 2 | import { fbs } from 'fbtee'; 3 | import { View } from 'react-native'; 4 | import Text from 'src/ui/Text.tsx'; 5 | 6 | export default function Index() { 7 | return ( 8 | <> 9 | 12 | 13 | 14 | Welcome 15 | 16 | 17 | Modern, sensible defaults, fast. 18 | 19 | 20 | 21 | 22 | Change{' '} 23 | 24 | src/app/(app)/(tabs)/index.tsx 25 | {' '} 26 | for live updates. 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/app/(app)/(tabs)/two.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import Text from 'src/ui/Text.tsx'; 3 | import useViewerContext from 'src/user/useViewerContext.tsx'; 4 | 5 | export default function Two() { 6 | const { logout } = useViewerContext(); 7 | 8 | return ( 9 | 10 | 11 | Logout 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/app/(app)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; 2 | import { Redirect, Stack } from 'expo-router'; 3 | import { Fragment } from 'react/jsx-runtime'; 4 | import useViewerContext from 'src/user/useViewerContext.tsx'; 5 | 6 | export default function TabLayout() { 7 | const { isAuthenticated, locale } = useViewerContext(); 8 | 9 | if (!isAuthenticated) { 10 | return ; 11 | } 12 | 13 | return ( 14 | 15 | 16 | 17 | 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/app/+html.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollViewStyleReset } from 'expo-router/html.js'; 2 | import { ReactNode } from 'react'; 3 | 4 | // This file is web-only and used to configure the root HTML for every 5 | // web page during static rendering. 6 | // The contents of this function only run in Node.js environments and 7 | // do not have access to the DOM or browser APIs. 8 | export default function Root({ children }: { children: ReactNode }) { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | {/* 16 | This viewport disables scaling which makes the mobile website act more like a native app. 17 | However this does reduce built-in accessibility. If you want to enable scaling, use this instead: 18 | 19 | */} 20 | 24 | {/* 25 | Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. 26 | However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. 27 | */} 28 | 29 | 30 | {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */} 31 |