🎉 I built a real-time Todo app in
ONE afternoon
- and you can too!

1. “Why can't I just build something that WORKS?”
Ever started a side-project and got stuck at:
- “Which auth library?”
- “Where do I host the DB?”
- “Why is Cognito crying again?”
Same here.
So I gave myself a 3-hour window to
ship auth + DB + real-time + dark-mode without opening
AWS or swearing at Docker.
Spoiler: we'll use Next.js 14 App Router + Firebase Emulators (runs 100 % on your laptop, costs $0, no credit card).

2. The stack - tiny, boring, bullet-proof
Layer | Tech | Why |
---|---|---|
UI | Next.js 14 App Router | Server components = free SSR |
Auth | Firebase Auth (email + Google) | Emulator = no quotas |
DB | Cloud Firestore (emulator) | Real-time out-of-the-box |
State | Zustand (client) | Less boiler-plate than Context |
Styling | Tailwind + shadcn/ui | Copy-paste components |
Deploy | Vercel (one-click) | Still $0 for hobby |
3. Scaffold in 90 seconds

npx create-next-app@latest next-todo-firebase --typescript --tailwind --app --src-dir --import-alias "@/*"
cd next-todo-firebase
npm i firebase firebase-admin zustand next-themes lucide-react
npx shadcn-ui@latest init -y
npx shadcn-ui@latest add button card input label separator dropdown-menu toast checkbox

4. Firebase Emulators - your local cloud

Create firebase.json 🔥:
{
"emulators": {
"auth": { "port": 9099 },
"firestore": { "port": 8080 },
"ui": { "enabled": true, "port": 4000 }
}
}
Start the “cloud” ☁:
firebase emulators:start --import ./emulator-data --export-on-exit
Open http://localhost:4000 - boom, local Firebase console.

5. Auth in 20 lines (seriously)

// app/(auth)/login/page.tsx
const { signInEmail, signInGoogle } = useAuthStore();
useAuthStore is just:
export const useAuthStore = create<AuthState>((set) => ({
signInEmail: async (e, p) => {
const token = await signInWithEmailAndPassword(auth, e, p);
await fetch("/api/session", { method: "POST", body: JSON.stringify({ token }) });
},
// ...
}));
No backend route for login
- we only
exchange the Firebase ID token for a cookie so
Server Components can read the user.

6. Server-side auth without a database call
We store the ID token in an
httpOnly
cookie (__session
).
Middleware
(Edge runtime) verifies it before React renders:
// middleware.ts
const user = await verifyTokenEdge(req.cookies.get("__session"));
if (!user) redirect("/login");
Edge = zero cold start, runs in every Vercel POP.

7. Real-time todos - subscribe, don't fetch
Client component:
useEffect(() => {
const unsub = onSnapshot(
collection(db, "users", uid, "todos"),
(snap) => setTodos(snap.docs.map((d) => ({ id: d.id, ...d.data() })))
);
return unsub;
}, [uid]);
Every device sees changes < 100 ms - no WebSocket boiler-plate.

8. Dark mode? One liner.
// providers.tsx
<NextThemesProvider attribute="class" defaultTheme="system">
Add a toggle anywhere:
<DropdownMenuItem onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
{theme === "dark" ? <Sun /> : <Moon />} Theme
</DropdownMenuItem>
Tailwind picks up dark:
variants automatically.

9. Deploy - push, done.
git push origin main
# Vercel GitHub integration builds & deploys
Your app is now live with:
- ✅ Server-side auth
- ✅ Real-time DB
- ✅ Dark mode
- ✅ Type-safety
- ✅ $0 monthly bill

10. Alternatives & “what if I…”
Instead of… | You could use… | Trade-off |
---|---|---|
Firebase Auth | NextAuth + GitHub/Google | More providers, more config |
Firestore | Supabase, PlanetScale, Mongo Atlas | SQL, relational, foreign keys |
Zustand | Redux-Toolkit, Jotai, Recoil | Bigger ecosystem, more boiler-plate |
Tailwind | Bootstrap (you asked!) |
Replace dark: with
data-theme="dark" and use Bootstrap classes
- same logic
|
Vercel | Netlify, Firebase Hosting, AWS Amplify | All offer Git-push deploys |
Bootstrap flavour (5 min swap):
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<button className="btn btn-primary">Add Todo</button>
<div className="card mb-3 p-3">…</div>
Dark mode in Bootstrap:
[data-theme="dark"] {
--bs-body-bg: #121212;
--bs-body-color: #eee;
}
Toggle with the same setTheme call - Bootstrap CSS variables react instantly.

11. Clone & hack
Repo:
https://github.com/sureshbabudj/next-todo-firebase
Commands:
git clone https://github.com/sureshbabudj/next-todo-firebase.git
cd next-todo-firebase
npm i
npm run emulators
npm run dev
Open http://localhost:3000
- happy shipping! 🥳

If this saved you a few hours, star the repo and share the article - let's keep building cool stuff without burning cash or brain-cells.
No comments:
Post a Comment