Auth is very important for any app these days.
Folder setup
install @react-native-firebase/app
, @react-native-firebase/auth
and firebase
src/firebase/firebase.tsx
import { Platform } from 'react-native';
import { getAuth, Auth, signInWithEmailAndPassword, createUserWithEmailAndPassword } from 'firebase/auth';
import auth from '@react-native-firebase/auth';
// Define the WebAuth interface
interface WebAuth {
signInWithEmailAndPassword: (email: string, password: string) => Promise<any>;
createUserWithEmailAndPassword: (email: string, password: string) => Promise<any>;
}
let firebaseApp;
let webAuth: WebAuth | undefined;
let getWebAuth: Auth;
if (Platform.OS === 'web') {
// Import Firebase modules for web
const { initializeApp } = require('firebase/app');
const firebaseConfig = {
apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.EXPO_PUBLIC_FIREBASE_MEASUREMENT_ID
};
// Initialize Firebase for web
firebaseApp = initializeApp(firebaseConfig);
getWebAuth = getAuth(firebaseApp);
webAuth = {
signInWithEmailAndPassword: (email: string, password: string) =>
signInWithEmailAndPassword(getWebAuth, email, password),
createUserWithEmailAndPassword: (email: string, password: string) =>
createUserWithEmailAndPassword(getWebAuth, email, password)
};
} else {
// Initialize Firebase for native platforms
const firebase = require('@react-native-firebase/app').default;
if (!firebase.apps.length) {
firebaseApp = firebase.initializeApp({});
} else {
firebaseApp = firebase.app();
}
webAuth = undefined;
}
export { firebaseApp, auth, webAuth, getWebAuth };
app/index.tsx
import { useState } from 'react';
import {
Text,
View,
StyleSheet,
KeyboardAvoidingView,
TextInput,
Button,
ActivityIndicator,
Platform,
Pressable,
} from 'react-native';
import { FirebaseError } from 'firebase/app';
import { auth, webAuth } from '../src/firebase/firebase';
import { Ionicons } from '@expo/vector-icons';
export default function Index() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const signUp = async () => {
setLoading(true);
try {
if (Platform.OS === 'web' && webAuth) {
await webAuth.createUserWithEmailAndPassword(email, password);
} else {
await auth().createUserWithEmailAndPassword(email, password);
}
alert('Check your emails!');
} catch (e: any) {
const err = e as FirebaseError;
alert('Registration failed: ' + err.message);
} finally {
setLoading(false);
}
};
const signIn = async () => {
setLoading(true);
try {
if (Platform.OS === 'web' && webAuth) {
await webAuth.signInWithEmailAndPassword(email, password);
} else {
await auth().signInWithEmailAndPassword(email, password);
}
} catch (e: any) {
const err = e as FirebaseError;
alert('Sign in failed: ' + err.message);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<KeyboardAvoidingView behavior="padding">
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
autoCapitalize="none"
keyboardType="email-address"
placeholder="Email"
/>
<View style={styles.passwordContainer}>
<TextInput
style={[styles.input, styles.passwordInput]}
value={password}
onChangeText={setPassword}
secureTextEntry={!showPassword}
placeholder="Password"
/>
<Pressable
onPress={() => setShowPassword(!showPassword)}
style={styles.eyeIcon}
>
<Ionicons
name={showPassword ? "eye-off" : "eye"}
size={24}
color="#666"
/>
</Pressable>
</View>
{loading ? (
<ActivityIndicator size={'small'} style={{ margin: 28 }} />
) : (
<>
<Button onPress={signIn} title="Login" />
<Button onPress={signUp} title="Create account" />
</>
)}
</KeyboardAvoidingView>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginHorizontal: 20,
flex: 1,
justifyContent: 'center'
},
input: {
marginVertical: 4,
height: 50,
borderWidth: 1,
borderRadius: 4,
padding: 10,
backgroundColor: '#fff'
},
passwordContainer: {
position: 'relative',
flexDirection: 'row',
alignItems: 'center',
},
passwordInput: {
flex: 1,
paddingRight: 45,
},
eyeIcon: {
position: 'absolute',
right: 10,
height: 50,
justifyContent: 'center',
},
});
app/_layout.tsx
import { Stack, useRouter, useSegments } from 'expo-router';
import { useEffect, useState } from 'react';
import { View, ActivityIndicator, Platform } from 'react-native';
import { auth , getWebAuth} from '../src/firebase/firebase';
type User = {
uid: string;
email: string | null;
} | null;
export default function RootLayout() {
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState<User>(null);
const router = useRouter();
const segments = useSegments();
const onAuthStateChanged = (user: User) => {
console.log('onAuthStateChanged', user);
setUser(user);
if (initializing) setInitializing(false);
};
useEffect(() => {
let unsubscribe;
if (Platform.OS === 'web') {
unsubscribe = getWebAuth.onAuthStateChanged(onAuthStateChanged);
} else {
// For native platforms, auth is already initialized
unsubscribe = auth().onAuthStateChanged(onAuthStateChanged);
}
return unsubscribe;
}, []);
useEffect(() => {
if (initializing) return;
const inAuthGroup = segments[0] === '(auth)';
if (user && !inAuthGroup) {
router.replace('/(auth)/home');
} else if (!user && inAuthGroup) {
router.replace('/');
}
}, [user, segments, initializing]);
if (initializing) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<Stack
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="index" options={{ title: 'Login' }} />
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
</Stack>
);
}
app/(auth)/_layout.tsx
import { Stack } from 'expo-router';
const Layout = () => {
return <Stack />;
};
export default Layout;
app/(auth)/home.tsx
import { View, Text, Button, Platform } from 'react-native';
import { auth,getWebAuth } from '../../src/firebase/firebase';
const Page = () => {
const user = Platform.OS === 'web' ? getWebAuth.currentUser : auth().currentUser;
const handleSignOut = async () => {
try {
if (Platform.OS === 'web') {
await getWebAuth.signOut();
} else {
await auth().signOut();
}
} catch (error) {
console.error('Sign out error:', error);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 18, marginBottom: 20 }}>
Welcome back {user?.email}
</Text>
<Button title="Sign out" onPress={handleSignOut} />
</View>
);
};
export default Page;
app.json
"plugins": [
"expo-router",
[
"expo-splash-screen",
{
"image": "./assets/images/splash-icon.png",
"imageWidth": 200,
"resizeMode": "contain",
"backgroundColor": "#ffffff"
}
],
"@react-native-firebase/app",
"@react-native-firebase/auth",
"@react-native-firebase/crashlytics",
[
"expo-build-properties",
{
"ios": {
"useFrameworks": "static"
}
}
]