Note: This is a crude example that is not mobile-friendly and is intended for demonstration purposes only.
This project demonstrates a modern React application using TanStack Router for navigation, Zustand for state management, and Firebase Authentication with GitHub OAuth.
- 🔐 Authentication - Complete GitHub OAuth flow using Firebase Auth
- 🧩 State Management - Clean and efficient state management with Zustand
- 🧭 Routing - Type-safe routing with TanStack Router
- 🔄 Persistence - Auth state persistence between sessions
- 🛡️ Protected Routes - Route protection based on authentication status
- 🔄 Loading States - Proper loading states during authentication
- React - UI library
- TanStack Router - Type-safe routing
- Zustand - State management
- Firebase - Authentication and backend services
- TypeScript - Type safety
- Vite - Build tool
Before you begin, ensure you have:
- Node.js 16+ installed
- A Firebase project created
- GitHub OAuth set up in your Firebase project
-
Create a Firebase project
-
Enable Authentication in the Firebase console
-
Add GitHub as an authentication provider:
- Go to Authentication > Sign-in method > GitHub
- Enable GitHub authentication
- You'll need to set up OAuth in your GitHub account:
- Go to GitHub Developer Settings
- Create a new OAuth app
- Set the homepage URL to your local or production URL
- Set the callback URL to:
https://your-firebase-project-id.firebaseapp.com/__/auth/handler
- Copy the Client ID and Client Secret
- Return to Firebase console and paste the GitHub Client ID and Client Secret
- Save the changes
-
Create a web app in your Firebase project:
- Go to Project Overview > Add app > Web
- Register the app with a nickname
- Copy the Firebase configuration object for later use
- Clone the repository:
git clone https://github.com/your-username/tanstack-router-firebase-auth.git
cd tanstack-router-firebase-auth
- Install dependencies:
npm install
- Create a
.env.local
file in the root directory and add your Firebase credentials:
VITE_FIREBASE_API_KEY=your-api-key
VITE_FIREBASE_AUTH_DOMAIN=your-project-id.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your-project-id
VITE_FIREBASE_STORAGE_BUCKET=your-project-id.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=your-messaging-sender-id
VITE_FIREBASE_APP_ID=your-app-id
Replace the values with your actual Firebase project credentials from the configuration object.
npm run dev
The application should now be running at http://localhost:5173
(or another port if 5173 is occupied).
This project follows a modular structure with clear separation of concerns:
/src
config.ts # Application configuration
/providers # Context providers
auth-context-provider.tsx # Authentication context provider
/routes # TanStack Router configuration
/_auth # Authentication related routes
/(public) # Publicly accessible routes
root.tsx # Root route definition
_auth.tsx # Auth route definition
index.tsx # Main routes index
/stores # Zustand stores
use-auth-store.ts # Authentication state management
main.tsx # Application entry point
routeTree.gen.tsx # Generated route tree
styles.css # Global styles
utils.ts # Utility functions
This application implements a complete authentication flow:
- Initialization: Firebase Auth is initialized on app load
- State Persistence: Authentication state is maintained across sessions
- Sign In: Users can sign in with their GitHub account
- Protected Routes: Certain routes require authentication
- Sign Out: Users can sign out, clearing their session
The application uses Zustand for state management. The main authentication store is defined in src/stores/use-auth-store.ts
:
import type { User } from "firebase/auth";
import { create } from "zustand";
export type AuthState = {
isAuthenticated: boolean;
user: User | null;
setUser: (user: User | null) => void;
};
export const useAuthStore = create<AuthState>((set) => ({
isAuthenticated: false,
user: null,
setUser: (user: User | null) => {
console.log(
"Auth state changed:",
user ? `User logged in: ${user.email || user.uid}` : "User logged out",
user
);
return set({
user,
isAuthenticated: !!user,
});
},
}));
The application uses TanStack Router for type-safe routing. The routes are organized in a modular structure with separate files for different route groups:
src/routes/index.tsx
- Main routes indexsrc/routes/root.tsx
- Root route definitionsrc/routes/_auth.tsx
- Authentication routessrc/routes/(public)/*
- Publicly accessible routes
Firebase is initialized in src/config.ts
:
import { initializeApp } from 'firebase/app'
import { getAuth, GithubAuthProvider } from 'firebase/auth'
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID,
}
// Initialize Firebase
const app = initializeApp(firebaseConfig)
// Initialize Firebase Auth
export const auth = getAuth(app)
- If you get initialization errors, make sure all environment variables are correctly set in
.env.local
- Double-check that you're using the correct prefix (
VITE_
) for all environment variables
- If you experience routing issues, ensure you've properly wrapped your application with the router provider
- Check that all routes are correctly defined and imported
This project is licensed under the MIT License - see the LICENSE file for details.