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

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).

gif-phew

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

gif-lego
            
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

gif-boom

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.

gif-localhost

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.

gif-magic

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.

gif-fast

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.

gif-sync

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.

gif-dark

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
gif-popcorn

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.

gif-finish

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! 🥳

gif-cheers

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.

Share the post on social network:

No comments:

Post a Comment