- Node 18
- Yarn 1.22.x
- eas-cli
- React Native
- Expo
- Firebase(Authentication, Firestore, Cloud Storage)
- expo
- firebase
- axios
- react-navigation
- expo-constants
- expo-image-manipulator
- expo-image-picker
- expo-notifications
- react-native-elements
- react-native-paper
- react-native-svg
- react-native-vector-icons
- jotai
- moment
- BottomTab, TopTab, Modal and Stack navigation
- Registration with E-mail & Password
- Login with E-mail and Password
- Change password
- Deleting an account
- Handling persisted login credentials
- Writing to Firestore Database
- Reading from Firestore Database
- Uploading images to Cloud Storage
- Update user profile
- Dynamic switching of dark themes by device appearance
- Dynamic header title
- Retrieving Expo push tokens and saving them to Firestore
- State management by Jotai
reactnative-expo-firebase-boilerplate v1
- use Context API
- Adjustment style file
Video guide(Japanese)
Download zip or click "Use this template"
or
git clone https://github.com/kiyohken2000/ReactNative-Expo-Firebase-Boilerplate-v2.git
cd ReactNative-Expo-Firebase-Boilerplate-v2
yarn install
Step 1: Create a Google Firebase Account
- Go to https://console.firebase.google.com/
- Sign up an account and create a new project
- Put in your project name, click "Continue".
- Google Analytics can be enabled or disabled.
- Click "Create Project" it is going to take a while before it says "Your new project is ready", click "Continue."
Step 2: Configure Firebase Console Sign-In Methods
- On "Project Overview", click the "Authentication" on the left sidebar.
- Click on the second tab where it said "Sign-In method"
- Enable all the Sign-in method you would like to enable. For now, we will enable the "Email/Password" option.
Step 3: Configure Firebase Console Firestore
- We will then click the "Cloud Firestore" on the left to create a database.
- Select the "Start in test mode" option, click "next."
- Select the "Cloud Firestore location", click "Enable"
- Create the "users" and "tokens" collections. An empty document is fine.
Security Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isLogin() {
return request.auth.uid != null;
}
match /{document=**} {
allow read, write: if isLogin();
}
}
}
Step 4: Create a folder in Cloud Storage
- Next. Click Storage on the left.
- Create an empty "avatar" folder.
- Upload the image file "icon.png" of your choice. This is the default icon for app users.
Security Rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
Step 5: Copy the API Key
- On the Project Overview, click on the "Web" icon since we are building on Expo.
- Then give it a name, click "Register app".
- It will show you the Firebase configuration, copy the SDK keys, we will use it in the next step.
Step 6: Connect Firebase
- Write the copied API Key in your app.
src\config.js
const firebaseKey = {
apiKey: "Your API Key",
authDomain: "Your API Key",
projectId: "Your API Key",
storageBucket: "Your API Key",
messagingSenderId: "Your API Key",
appId: "Your API Key",
measurementId: "Your API Key"
};
Replace the name and Slug with yours.
"name": "your-app-name",
"slug": "your-app-slug",
Remove expo.updates.url
"updates": {
"enabled": true,
"fallbackToCacheTimeout": 10000,
"url": "https://u.expo.dev/1650611b-a5b8-4420-9656-60c06429edd4"
},
Remove expo.extra
and expo.runtimeVersion
"extra": {
"eas": {
"projectId": "1650611b-a5b8-4420-9656-60c06429edd4"
}
},
"runtimeVersion": {
"policy": "appVersion"
}
src\config.js
const defaultAvatar = 'Your default icon URL'
const expoProjectId = 'Your project id'
yarn start
import { Restart } from '../../utils/Restart'
const ourFunc = async() => {
await Restart()
}
import { storage } from '../../utils/Storage'
const saveStorage = async() => {
const today = moment().toString()
await storage.save({
key: 'date',
data: {
'date': today
}
})
}
const loadStorage = async() => {
try {
const result = await storage.load({key: 'date'})
} catch (e) {
console.log(e)
}
}
const removeStorage = async() => {
await storage.remove({ key: 'date' })
}
// Toast
import { showToast } from '../../utils/ShowToast'
const onShowToastPress = () => {
showToast({
title: 'Hello',
body: 'This is some something ๐',
isDark // true or false
})
}
// Send Notification
import { sendNotification } from '../../utils/SendNotification'
const onNotificationPress = async() => {
const res = await sendNotification({
title: 'Hello',
body: 'This is some something ๐',
data: 'something data',
token: token.token //e.g. ExponentPushToken[WGSdXiJ5rLHAK53DRPq2x-]
})
console.log(res)
}
import React, { useState } from 'react'
import { View, StyleSheet } from 'react-native'
import ScreenTemplate from '../../components/ScreenTemplate'
import Button from '../../components/Button'
import ShowSnackbar from '../../components/ShowSnackbar' // import snackbar component
export default function Follower() {
const [visible, setVisible] = useState(false) // create snackbar state
const onDismissSnackBar = () => setVisible(false) // create hide snackbar function
const onShowSnackbarPress = () => { // create show snackbar function
setVisible(true)
}
return (
<>
<ScreenTemplate>
<View style={styles.container}>
<View style={{width:'100%'}}>
<Button
label='Show Snackbar'
color={colors.purple}
onPress={onShowSnackbarPress}
/>
</View>
</View>
</ScreenTemplate>
{/* Pass props to snackbar component */}
<ShowSnackbar
visible={visible}
onDismissSnackBar={onDismissSnackBar}
title='Hello ๐'
duration={3000}
/>
</>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
})
export default function Print() {
const [data, setData] = useState([])
const [isLoading, setIsLoading] = useState(false) // Create loading flag
const [isError, setIsError] = useState(false) // Create error flag
const fetchData = async() => {
try {
setIsLoading(true) // Set flag
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
setData(data)
} catch(e) {
console.log('error', e)
setIsError(true) // Set flag
} finally {
setIsLoading(false) // Set flag
}
}
return (
<ScreenTemplate isLoading={isLoading} isError={isError}> {/* Pass flag to ScreenTemplate component */}
<ScrollView style={styles.main}>
{data.map((item, i) => {
return (
<RenderItem item={item} key={i} index={i} />
)
})}
</ScrollView>
</ScreenTemplate>
)
}
The experienceId is required to get the push token using getExpoPushTokenAsync in the EAS build.
const token = await Notifications.getExpoPushTokenAsync({
projectId: 'your project id'
});
If you want to use environment variables in EAS build, create .easignore.
How projects are uploaded to EAS Build
SDK47 and later do not support classic build. You must use EAS build to build your application.
- expo account
- eas-cli
npx expo install expo-dev-client
eas update:configure
eas build:configure
example
eas.json
{
"cli": {
"version": ">= 3.15.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"channel": "development",
"ios": {
"simulator": false
}
},
"preview": {
"distribution": "internal",
"channel": "internal"
},
"production": {
"channel": "production"
}
},
"submit": {
"production": {}
}
}
eas device:create
eas build --profile development --platform ios
eas build --profile development --platform android
yarn start
Scan the QR code above with Expo Go (Android) or the Camera app (iOS)
// build: develop
eas build --profile development --platform ios
eas build --profile development --platform android
// build: internal
eas build --profile preview --platform ios
eas build --profile preview --platform android
// build: production
eas build --profile production --platform ios
eas build --profile production --platform android
// OTA update
eas update --channel internal --message "Updating the app"
eas update --channel production --message "Updating the app"
// iOS: Setting up ad hoc provisioning
eas device:create
// list all registered devices for your account
eas device:list
// Expo Settings
eas build:configure
eas update:configure
This project is available under the MIT license. See the LICENSE file for more info.