A full-featured cross-platform todo app built with React Native (Expo) and TypeScript, backed by Firebase for authentication and real-time data. Users sign up, log in, and manage a personal todo list that syncs live across sessions and devices.
- Authentication — email/password sign up & login with Firebase Auth; the UI reacts automatically to the auth state
- Todos CRUD — create, edit, complete, and delete todos with a title, description, and deadline
- Real-time sync — todos update instantly via Firestore
onSnapshot, scoped per user - Image attachments — pick an image for a todo with the device image picker
- Deadlines — native date/time picker per todo
- Dark mode — toggle persisted with Redux + redux-persist
- Account settings — change password from the settings screen
- Secure by design — Firestore security rules ensure each user can only read/write their own data
| Layer | Choice |
|---|---|
| Framework | Expo (managed) + TypeScript |
| Backend | Firebase Auth + Cloud Firestore |
| State | Redux Toolkit + redux-persist |
| Data fetching | TanStack React Query |
| Navigation | React Navigation (drawer + bottom tabs + stacks) |
| Forms & validation | Formik + Yup |
| Styling | NativeWind (Tailwind CSS) + custom components |
| Media / pickers | expo-image-picker, @react-native-community/datetimepicker |
| Fonts | Custom Montserrat via expo-font |
src/
components/ reusable UI (buttons, inputs, list item)
config/ firebase initialization
hooks/ useUserTodos (Firestore CRUD), useTodoForm, useDarkMode, redux helpers
navigation/ auth stack, app drawer, tabs, nested stacks
screens/ Login, Register, Home, AddTodo, EditTodo, Settings
store/ Redux store + slices
styles/ per-screen StyleSheet definitions
git clone <repo-url>
cd todo-app
yarn install
npx expo startThen press a for an Android emulator, i for iOS, or scan the QR code with Expo Go.
The app ships with a Firebase config in src/config/firebase.ts. To point it at your own backend:
- Create a project at console.firebase.google.com
- Add a Web app and copy the config object into
src/config/firebase.ts - Enable Authentication → Email/Password
- Create a Firestore database and publish these rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
match /todos/{todoId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
}
A Firebase web
apiKeyis safe to commit — it identifies the project, it does not grant access. Access is controlled by the security rules above.
State management — Redux Toolkit holds app-wide UI state (e.g. dark mode) and persists it with redux-persist, so preferences survive restarts. Server state (todos) is owned by Firestore and streamed in real time via onSnapshot rather than being duplicated in Redux — each tool does what it's best at.
Data isolation — every Firestore read/write is namespaced under users/{uid}, and the security rules enforce that a signed-in user can only touch their own subtree. The client and the rules agree on the same shape.
Auth-driven navigation — a single onAuthStateChanged listener at the app root swaps between the auth stack and the main app, so navigation always reflects the real session.



