A full-stack, Notion-inspired document editor built with React 18, Zustand, and Firebase. Features real-time cloud sync, Google + email authentication, offline support, drag-and-drop reordering, and full version history โ all deployable to the web with zero local development environment required.
Deployed on Vercel โ mini-notion-nine.vercel.app
| Feature | Description |
|---|---|
| โ๏ธ Cloud Sync | Real-time Firestore sync โ edits appear across all devices instantly |
| ๐ Authentication | Google OAuth + Email/Password sign-in via Firebase Auth |
| ๐ด Offline Support | IndexedDB persistence โ the app works fully without internet |
| ๐ Nested Documents | Create pages inside pages with unlimited hierarchy depth |
| ๐ Drag & Drop | Reorder pages in the sidebar โ persisted to Firestore |
| ๐พ Autosave | Debounced 800ms autosave with optimistic updates |
| โฐ Version History | Up to 25 restorable cloud snapshots per document |
| ๐๏ธ Role System | Toggle between Editor and Read-only mode |
| ๐ Markdown | Full Markdown editing with live preview |
| ๐จ Emoji Icons | Per-page emoji picker |
| โจ๏ธ Keyboard Shortcuts | Ctrl+B sidebar toggle, Ctrl+N new page |
| Layer | Technology |
|---|---|
| UI Framework | React 18 with Hooks |
| State Management | Zustand + Immer |
| Cloud Database | Firebase Firestore |
| Authentication | Firebase Auth (Google + Email/Password) |
| Offline Persistence | Firestore IndexedDB cache |
| Drag & Drop | HTML5 native drag-and-drop |
| Build Tool | Vite |
| Deployment | Vercel |
mini-notion/
โโโ .env.example # Firebase env vars template (safe to commit)
โโโ index.html # App entry point
โโโ package.json
โโโ vite.config.js
โโโ tailwind.config.js
โโโ postcss.config.js
โ
โโโ src/
โโโ main.jsx # React root
โโโ App.jsx # Auth gate + Firestore sync bootstrap
โโโ index.css # Global styles + animations
โ
โโโ firebase/
โ โโโ config.js # Firebase init + IndexedDB offline persistence
โ โโโ auth.js # Auth helpers: Google, email, sign-out
โ โโโ firestore.js # All Firestore reads, writes, listeners
โ
โโโ store/
โ โโโ documentStore.js # Zustand: documents, versions, Firebase writes
โ โโโ uiStore.js # Zustand: role, sidebar, selection state
โ
โโโ hooks/
โ โโโ useAuth.js # Firebase auth state observer
โ โโโ useFirestoreSync.js # Bootstrap + real-time onSnapshot subscriptions
โ โโโ useAutosave.js # Debounced Firestore persistence hook
โ โโโ useDebounce.js # Generic debounce hook
โ
โโโ utils/
โ โโโ immutable.js # Immutable tree helpers (Immer patterns)
โ โโโ dateUtils.js # Timestamp formatting utilities
โ
โโโ components/
โโโ Auth/
โ โโโ AuthScreen.jsx # Sign-in / Sign-up / Password reset UI
โโโ Header/
โ โโโ Header.jsx # Top bar: sync status, user menu, sign-out
โโโ RoleToggle/
โ โโโ RoleToggle.jsx # Editor / Read-only segmented control
โโโ EmojiPicker/
โ โโโ EmojiPicker.jsx # Floating emoji grid
โโโ Sidebar/
โ โโโ Sidebar.jsx # Sidebar shell + New Page button
โ โโโ DocumentItem.jsx # Draggable, collapsible tree node
โโโ Editor/
โ โโโ Editor.jsx # Main editor + autosave orchestration
โ โโโ EditorToolbar.jsx # Markdown formatting toolbar
โ โโโ MarkdownView.jsx # Read-only Markdown renderer
โโโ VersionHistory/
โโโ VersionPanel.jsx # Version list + Firestore restore
This project is designed to be deployed entirely through the browser โ no local development environment needed.
Create a Firebase project:
- Go to firebase.google.com and sign in with your Google account
- Click Create a project, name it
mini-notion, disable Google Analytics, click Create - Once ready, click Continue
Register a Web App:
- On the project dashboard, click the Web icon (
</>) - Give it a nickname like
mini-notion-weband click Register app - Copy the
firebaseConfigvalues shown โ you'll need these in Step 3
Enable Authentication:
- Go to Build โ Authentication โ Get started
- Click Google โ toggle Enable โ add your support email โ Save
- Click Email/Password โ toggle Enable โ Save
Create Firestore Database:
- Go to Build โ Firestore Database โ Create database
- Choose Production mode, pick a region close to you, click Enable
- Go to the Rules tab and replace the contents with:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{userId}/{document=**} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
- Click Publish
- Go to github.com โ New repository โ name it
mini-notion - Upload the contents of the
mini-notion-firebase.zip(unzipped) to the repo - Make sure
package.jsonandindex.htmlare at the root level โ ๏ธ Do not upload.env.localโ only.env.exampleis safe to commit
- Go to vercel.com โ Sign up with GitHub
- Click Add New โ Project โ import your
mini-notionrepository - Vercel auto-detects Vite โ the default settings are correct
- Before clicking Deploy, scroll to Environment Variables and add all 6 Firebase values:
| Variable Name | Where to find the value |
|---|---|
VITE_FIREBASE_API_KEY |
Firebase โ Project Settings โ Your App |
VITE_FIREBASE_AUTH_DOMAIN |
Firebase โ Project Settings โ Your App |
VITE_FIREBASE_PROJECT_ID |
Firebase โ Project Settings โ Your App |
VITE_FIREBASE_STORAGE_BUCKET |
Firebase โ Project Settings โ Your App |
VITE_FIREBASE_MESSAGING_SENDER_ID |
Firebase โ Project Settings โ Your App |
VITE_FIREBASE_APP_ID |
Firebase โ Project Settings โ Your App |
- Click Deploy โ your live URL will be ready in about 60 seconds
Google sign-in requires two separate places to be updated. Both are required.
In Firebase Console:
- Go to Authentication โ Settings โ Authorized domains
- Click Add domain and enter your Vercel URL without
https://:your-app.vercel.app
In Google Cloud Console:
- Go to console.cloud.google.com
- Select your Firebase project from the top-left dropdown
- Go to APIs & Services โ Credentials
- Click the Web client (auto created by Google Service) OAuth 2.0 client
- Under Authorized JavaScript origins click Add URI and enter:
https://your-app.vercel.app - Under Authorized redirect URIs click Add URI and enter:
https://your-app.vercel.app/__/auth/handler - Click Save and wait 5 minutes for the changes to take effect
- Open your Vercel URL
- Sign in with Google or create an email/password account
- Create a page, type something, and look for โ Saved to cloud in the header
- Open Firebase Console โ Firestore Database โ Data โ a
workspacescollection will appear with your user ID inside it
The database starts completely empty โ this is normal. Firestore creates all collections and documents automatically the first time a user signs in.
Once deployed, all code changes follow this simple workflow:
- Open any file in your GitHub repository
- Click the pencil โ๏ธ icon to edit it directly in the browser
- Make your changes and click Commit changes
- Vercel automatically detects the new commit and redeploys in ~60 seconds
- Your live URL updates โ no terminal, no local setup required
Firestore
โโโ workspaces/
โโโ {userId}/ โ isolated workspace per user
โโโ meta/
โ โโโ workspace โ { rootDocumentIds: string[] }
โโโ documents/
โ โโโ {docId} โ document content + metadata
โโโ versions/
โโโ {docId}/
โโโ snapshots/
โโโ {snapId} โ immutable version snapshot
Each user gets a fully isolated workspace. The Firestore security rules ensure users can only access their own data โ no user can read or write another user's workspace.
The UI never waits for Firestore. Every change is applied to the Zustand store immediately, then persisted to Firestore after an 800ms debounce. The editor always feels instant regardless of network speed.
User types
โ Local Zustand state updates instantly (no lag)
โ [800ms pause after last keystroke]
โ Firestore write fires
โ Version snapshot saved
โ "โ Saved to cloud" indicator shown
If a remote Firestore update arrives while the user is actively editing, the local version is preserved when its updatedAt timestamp is newer. This prevents cloud sync from overwriting unsaved in-progress edits.
enableIndexedDbPersistence() in firebase/config.js caches all Firestore data locally. When offline, reads are served from cache and writes are queued. Everything syncs automatically the moment connectivity is restored โ no extra code required.
| Shortcut | Action |
|---|---|
Ctrl / โ + B |
Toggle sidebar open and closed |
Ctrl / โ + N |
Create a new root-level page |
| Auto | All edits save 800ms after you stop typing |
| Problem | Most Likely Cause | Fix |
|---|---|---|
| Google button โ "This site can't be reached" | Missing OAuth origins in Google Cloud Console | Add your Vercel domain to Authorized JavaScript Origins in Google Cloud โ APIs & Services โ Credentials |
auth/unauthorized-domain in console |
Vercel domain not added to Firebase | Firebase โ Auth โ Settings โ Authorized Domains โ Add domain |
auth/popup-closed-by-user |
Browser blocked the popup | Disable popup blocker for your site, or switch to signInWithRedirect |
Firebase values showing as undefined |
Env vars missing or app not redeployed | Vercel โ Settings โ Environment Variables โ verify all 6 exist โ Redeploy |
| Firestore data tab stays empty | Rules not published or auth failing | Re-publish Firestore security rules; check F12 console for auth errors |
| Build fails on Vercel | package.json not at repo root |
Ensure files were uploaded to repo root, not inside a subfolder |
| "โ Saved to cloud" never appears | Firestore write failing silently | Open F12 โ Console and look for Firebase error messages |
| Data disappears after page refresh | Firestore write not completing before navigation | Check F12 โ Network tab for failed requests to Firestore |
| Google sign-in works locally but not on Vercel | Production domain not authorized | Repeat Step 4 with your exact Vercel URL |
- Real-time collaboration โ Yjs + y-firestore for CRDT-based concurrent editing with live cursors
- Rich text editor โ TipTap or Slate.js replacing the plain
<textarea> - Workspace sharing โ invite other users with editor or read-only roles
- Full-text search โ Algolia or Typesense connected via Cloud Functions
- Export โ download any page as Markdown or PDF
- Mobile responsive layout โ collapsible sidebar for phones and tablets
- Tests โ Vitest unit tests for store actions, Playwright E2E tests for key flows
Built with the Catppuccin Mocha dark color palette.
| Token | Hex | Usage |
|---|---|---|
| Base | #1e1e2e |
Main editor background |
| Mantle | #181825 |
Sidebar and toolbar backgrounds |
| Crust | #11111b |
Header bar |
| Surface 0 | #313244 |
Buttons and input backgrounds |
| Overlay 0 | #6c7086 |
Muted text and icons |
| Text | #cdd6f4 |
Primary text |
| Mauve | #cba6f7 |
Accent color, active selections |
| Green | #a6e3a1 |
Save confirmation, success states |
| Yellow | #f9e2af |
Saving in progress indicator |
| Red | #f38ba8 |
Delete actions and error states |
MIT โ free to use, modify, and distribute.
Built with React 18 ยท Zustand ยท Firebase ยท Vite ยท Deployed on Vercel