Authentication
It's common to restrict certain routes to users who are not authenticated. This can be achieved in a very organized way by using React Context and Route Groups.
Consider the following project:
app/
_layout.js
index.js
(auth)/
sign-in.js
First, we'll setup a React Context provider that we can use to protect routes. This provider will use a mock implementation, you can replace it with your own authentication provider.
import { useRouter, useSegments } from "expo-router";
import React from "react";
const AuthContext = React.createContext(null);
// This hook can be used to access the user info.
export function useAuth() {
return React.useContext(AuthContext);
}
// This hook will protect the route access based on user authentication.
function useProtectedRoute(user) {
const segments = useSegments();
const router = useRouter();
React.useEffect(() => {
const inAuthGroup = segments[0] === "(auth)";
if (
// If the user is not signed in and the initial segment is not anything in the auth group.
!user &&
!inAuthGroup
) {
// Redirect to the sign-in page.
router.replace("/sign-in");
} else if (user && inAuthGroup) {
// Redirect away from the sign-in page.
router.replace("/");
}
}, [user, segments]);
}
export function Provider(props) {
const [user, setAuth] = React.useState(null);
useProtectedRoute(user);
return (
<AuthContext.Provider
value={{
signIn: () => setAuth({}),
signOut: () => setAuth(null),
user,
}}
>
{props.children}
</AuthContext.Provider>
);
}
Now we can use this context to control the access to the routes, we'll do this by using a Layout Route that wraps all the screens which require authentication.
import { Slot } from "expo-router";
import { Provider } from "../context/auth";
export default function Root() {
return (
// Setup the auth context and render our layout inside of it.
<Provider>
<Slot />
</Provider>
);
}
Now we can create our (auth)
group which is unprotected, this screen can toggle the authentication using signIn()
.
import { Text, View } from "react-native";
import { useAuth } from "../../context/auth";
export default function SignIn() {
const { signIn } = useAuth();
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text onPress={() => signIn()}>Sign In</Text>
</View>
);
}
And finally we'll implement an authenticated screen which can sign out.
import { Text, View } from "react-native";
import { useAuth } from "../context/auth";
export default function Index() {
const { signOut } = useAuth();
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text onPress={() => signOut()}>Sign Out</Text>
</View>
);
}
Now if the authentication state changes globally, the user will be redirected to the appropriate route.