A real-time collaborative document workspace for modern teams.
Write together in a rich block editor, organize work into workspaces, discuss ideas in threaded comments with @mentions, and stay in the loop with an inbox for notifications. It works on desktop, tablet and mobile.
Live Demo · Report a Bug · Request a Feature
- About the Project
- Screenshots
- Key Features
- Tech Stack
- Architecture
- Getting Started
- Environment Variables
- Usage
- Project Structure
- Data Model
- Available Scripts
- Deployment
- Security
- Roadmap
- Contributing
- License
- Acknowledgements
Document Planner is a full-stack, Notion-style collaboration app built with the Next.js App Router. Teams create workspaces, fill them with documents, and edit them together in real time. Every document has a cover image, an emoji and a block-based editor. Comment threads, @mentions and an inbox keep the whole team in sync.
It shows how to put together a production-grade stack:
- Clerk for authentication and organizations
- Cloud Firestore for storage and live sync
- Liveblocks for comments, mentions and notifications
- Editor.js for rich, structured content
| Problem | How Document Planner solves it |
|---|---|
| Notes and plans are scattered across tools | One workspace per team or project, with every document in it |
| Feedback gets lost in chat apps | Threaded comments and @mentions right next to the content |
| You never know what changed while you were away | A notifications inbox with an unread counter |
| Personal and team work get mixed | Clerk Organizations keep personal and team workspaces separate |
| Collaboration tools feel clunky on phones | A fully responsive UI with a mobile drawer navigation |
- Secure sign-in and sign-up with Clerk (email, Google and other social providers)
- Organization switcher: move between personal and team spaces
- Protected routes for the dashboard, workspaces and API
- Create workspaces with a name, emoji and cover image
- Switch between a grid and a list view on the dashboard
- Rename or delete workspaces (deleting removes all their documents)
- Workspaces belong to the active organization or to your personal account
- Create, rename, delete (with confirmation) and share documents
- A cover image and emoji for each document, plus an inline-editable title
- Shareable links: copy a direct URL in one click
Built on Editor.js, with these blocks:
Headings · Paragraphs · Bulleted & numbered lists · Checklists · Tables · Code blocks · Alerts · Delimiters · Images
- Autosave: changes are debounced and saved to Firestore
- Smooth live sync: block-level merging shows collaborators' edits instantly without moving your cursor or closing the mobile keyboard
- Threaded comments powered by Liveblocks
- @mentions with autocomplete, limited to members of the workspace organization
- Notifications inbox on the dashboard and in workspaces: shows document names, links straight to the comment and clears the unread badge when opened
- A slide-in sidebar drawer on mobile, and a fixed sidebar on desktop
- Headers, dialogs, the editor, comments and notifications adapt to every screen size
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack, Server Components) |
| UI Library | React 19 |
| Styling | Tailwind CSS 4, shadcn/ui, Radix UI |
| Authentication | Clerk (users, sessions, organizations) |
| Database | Cloud Firestore (Firebase JS SDK v12, Admin SDK v13) with security rules |
| Real-time | Liveblocks (comments, mentions, inbox notifications) |
| Editor | Editor.js and plugins |
| Icons | Lucide |
| Toasts | Sonner |
| Emoji Picker | emoji-picker-react |
| Hosting | Vercel |
flowchart LR
U["👤 User (Browser)"] -->|Sign in| C[Clerk]
U -->|Pages & UI| N["Next.js 16 App"]
N -->|proxy.js route protection| C
U -->|Exchange Clerk session| T["/api/firebase-token"]
T -->|Custom token with<br/>email + org claims| U
U <-->|Read / write documents<br/>enforced by security rules| F[("Cloud Firestore")]
U <-->|Comments, mentions,<br/>notifications| L[Liveblocks]
L -->|Request access token| A["/api/liveblocks-auth"]
A -->|Verify session| C
A -->|Check workspace access<br/>via Admin SDK| F
How a document session works:
- Clerk signs the user in.
proxy.jsblocks protected routes for anyone who isn't signed in. - Firebase sign-in:
/api/firebase-tokenexchanges the Clerk session for a Firebase custom token that carries the user's email and organization ids. The app signs in to Firebase with it, and Firestore security rules only allow access to workspaces owned by that email or organizations. - The workspace layout loads the sidebar and starts a live Firestore listener for the workspace's documents.
- Opening a document joins a Liveblocks room whose id is the document id. Before it issues a token,
/api/liveblocks-authchecks that the user owns the workspace or belongs to its organization. - Editor.js loads the saved content from Firestore and autosaves changes after a short delay. Collaborators' edits are merged block by block, so your cursor and mobile keyboard are never disturbed.
| Requirement | Version / Notes |
|---|---|
| Node.js | 20.9 or newer |
| npm | Comes with Node.js |
| Clerk account | Free tier is enough |
| Firebase project | Cloud Firestore + Authentication enabled |
| Liveblocks account | Free tier is enough |
1. Clone the repository
git clone https://github.com/ethical0101/Document-Planner.git
cd Document-Planner2. Install dependencies
npm install3. Configure environment variables
cp .env.example .env.localThen fill in your keys. See Environment Variables.
4. Set up the services
Clerk
- Create an application in the Clerk Dashboard.
- Enable the sign-in methods you want (Email, Google, ...).
- Turn on Organizations under Configure → Organizations.
- Copy the Publishable key and Secret key into
.env.local.
Firebase
- Create a project in the Firebase Console.
- Add a Web app and copy its config values into the
NEXT_PUBLIC_FIREBASE_*variables. - Enable Cloud Firestore.
- Open Authentication and click Get started. This enables Firebase Auth, which the app uses with custom tokens; no sign-in provider needs to be turned on.
- Go to Project settings → Service accounts → Generate new private key. From the downloaded JSON, copy
project_id,client_emailandprivate_keyintoFIREBASE_PROJECT_ID,FIREBASE_CLIENT_EMAILandFIREBASE_PRIVATE_KEY. - Deploy the security rules from
firestore.rules:npx firebase-tools login npx firebase-tools deploy --only firestore:rules --project <your-project-id>
- Upgrading an existing project? Run the one-time migration so older documents get their
orgId(dry run first, then apply):npm run migrate npm run migrate -- --apply
Liveblocks
- Create a project in the Liveblocks Dashboard.
- Copy the Secret key into
LIVEBLOCK_SKin.env.local.
5. Start the development server
npm run devOpen http://localhost:3000 in your browser. 🎉
Create a .env.local file in the project root. Use .env.example as the template.
| Variable | Required | Scope | Description |
|---|---|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
✅ | Client | Clerk publishable key |
CLERK_SECRET_KEY |
✅ | Server | Clerk secret key |
NEXT_PUBLIC_CLERK_SIGN_IN_URL |
✅ | Client | Sign-in route, for example /sign-in |
NEXT_PUBLIC_CLERK_SIGN_UP_URL |
✅ | Client | Sign-up route, for example /sign-up |
NEXT_PUBLIC_FIREBASE_API_KEY |
✅ | Client | Firebase web API key |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN |
➖ | Client | Firebase auth domain |
NEXT_PUBLIC_FIREBASE_PROJECT_ID |
➖ | Client | Firebase project id |
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET |
➖ | Client | Firebase storage bucket |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID |
➖ | Client | Firebase messaging sender id |
NEXT_PUBLIC_FIREBASE_APP_ID |
➖ | Client | Firebase app id |
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID |
➖ | Client | Google Analytics measurement id |
FIREBASE_PROJECT_ID |
✅ | Server | Service account project id |
FIREBASE_CLIENT_EMAIL |
✅ | Server | Service account client email |
FIREBASE_PRIVATE_KEY |
✅ | Server | Service account private key (` |
| `-escaped) | |||
LIVEBLOCK_SK |
✅ | Server | Liveblocks secret key |
Caution
Never commit .env.local or any file that contains secret keys. All .env* files
except .env.example are git-ignored.
- Sign up or sign in from the landing page.
- (Optional) Create or switch organization with the organization switcher to collaborate as a team.
- On the dashboard, click New Workspace. Pick a name, an emoji and a cover image.
- In the workspace, use + in the sidebar to add documents.
- Write in the editor. Press Tab or click + to insert blocks such as headings, lists, tables and code.
- Open the 💬 comments button to start a thread and @mention teammates.
- Check the 🔔 bell for your notifications.
- Use Share to copy a link to the document.
| Shortcut | Action |
|---|---|
| Ctrl/⌘ + Shift + L | Insert list |
| Ctrl/⌘ + Shift + C | Insert checklist |
| Ctrl/⌘ + Shift + P | Insert code block |
| Ctrl/⌘ + Shift + A | Insert alert |
Document-Planner/
├── app/
│ ├── (auth)/ # Clerk sign-in & sign-up pages
│ ├── (routes)/
│ │ ├── createworkspace/ # Create a new workspace
│ │ ├── dashboard/ # Workspace overview (grid / list views)
│ │ └── workspace/
│ │ ├── [workspaceid]/
│ │ │ ├── layout.jsx # Workspace shell (sidebar + Liveblocks)
│ │ │ ├── page.jsx # Opens the first document
│ │ │ └── [documentid]/ # Document editor page
│ │ └── _components/ # Sidebar, editor, comments, notifications…
│ ├── _components/ # Landing page, logo, pickers, auth layout
│ ├── api/
│ │ ├── firebase-token/ # Mints Firebase custom tokens from Clerk sessions
│ │ ├── liveblocks-auth/ # Authorizes users for Liveblocks rooms
│ │ └── mention-suggestions/ # Workspace members for @mentions
│ ├── Room.jsx # Liveblocks client & room providers
│ ├── globals.css # Tailwind CSS 4 theme & global styles
│ └── layout.js # Root layout (Clerk, fonts, toaster, metadata)
├── components/
│ ├── FirebaseAuthGate.jsx # Signs in to Firebase with the Clerk session
│ └── ui/ # shadcn/ui primitives
├── config/firebaseConfig.js # Firebase initialization
├── docs/screenshots/ # README screenshots
├── lib/ # Shared helpers (documents, block sync, utils)
│ └── server/ # Server-only helpers (Firebase Admin, access checks)
├── public/Assets/ # Logo, cover images & illustrations
├── scripts/migrate-org-ids.mjs # One-time Firestore data migration
├── firestore.rules # Firestore security rules
├── proxy.js # Clerk route protection (Next.js proxy)
├── next.config.mjs # Next.js configuration
├── .env.example # Environment variable template
└── package.json
The app uses four Firestore collections:
erDiagram
Workspace ||--o{ workspaceDocuments : contains
workspaceDocuments ||--|| documentOutput : "has content"
DocPlannerUsers ||--o{ Workspace : creates
Workspace {
string id PK
string workspaceName
string emoji
string coverImage
string createdBy
string orgId "Clerk org id or user email"
}
workspaceDocuments {
string id PK
string workspaceId FK
string documentName
string emoji
string coverImage
string orgId "copied from workspace"
string createdBy
}
documentOutput {
string docId PK
string orgId "copied from workspace"
string output "Editor.js JSON"
string editedBy
}
DocPlannerUsers {
string email PK
string name
string avatar
}
Each Liveblocks room id is the same as its document id, so every document has its own comments and presence.
| Command | Description |
|---|---|
npm run dev |
Start the development server with hot reload |
npm run build |
Create an optimized production build |
npm run start |
Serve the production build |
npm run migrate |
One-time data migration for Firestore rules (add -- --apply to write) |
- Click the button above, or import the repository at vercel.com/new.
- Add all the required environment variables. Paste
FIREBASE_PRIVATE_KEYexactly as it appears in the service account JSON. - Deploy the Firestore rules and, for existing data, run the migration. See the Firebase setup steps above.
- Set Node.js 20.x or newer under Project Settings → General.
- Add your production domain in the Clerk Dashboard under Domains.
- Deploy. 🚀
Any platform that supports Next.js 16 on Node.js 20.9 or newer works as well (Netlify, Railway, Render, Docker and others).
- ✅ 0 known vulnerabilities: all dependencies are on patched versions (
npm audit). - ✅ Secrets stay server-side:
CLERK_SECRET_KEY,FIREBASE_PRIVATE_KEYandLIVEBLOCK_SKare never sent to the browser. - ✅ Clerk ⇄ Firebase integration: users sign in to Firebase with a custom token minted from their Clerk session. The token carries their email and organization ids.
- ✅ Firestore security rules (
firestore.rules): users can only read and write workspaces, documents and content that belong to them or to their organizations. Ownership can't be changed or spoofed, and every other collection is closed. The rules are covered by tests run against the Firestore emulator. - ✅ Route protection:
/dashboard,/workspace,/createworkspaceand the/api/*routes require a signed-in user. - ✅ Scoped @mentions: suggestions only include members of the document's workspace organization (or just the owner for personal workspaces).
- ✅ Room authorization: the Liveblocks auth endpoint validates the room id and checks that the user owns the workspace or belongs to its organization before it grants access.
- ✅ No secrets in git:
.env*files are ignored, and.env.examplecontains placeholders only.
Important
Remember to deploy firestore.rules to your Firebase project. Until the rules are
deployed, Firestore uses whatever rules are currently set in the console.
Reporting a vulnerability: please don't open a public issue. Use GitHub private vulnerability reporting instead.
- Authentication & organizations
- Workspaces with emoji & cover images
- Rich block editor with autosave
- Real-time sync between collaborators
- Threaded comments, @mentions & notifications
- Fully responsive UI
- Clerk ⇄ Firebase Auth integration with strict Firestore rules
- Workspace rename & delete
- Workspace member management
- Document search
- Dark mode
- Export to PDF / Markdown
- Document version history
Have an idea? Open a feature request.
Contributions make the open-source community great, and any contribution is appreciated.
- Fork the repository.
- Create a feature branch:
git checkout -b feature/amazing-feature
- Commit your changes, following Conventional Commits:
git commit -m "feat: add amazing feature" - Push the branch:
git push origin feature/amazing-feature
- Open a pull request.
Please make sure npm run build passes before you submit.
Distributed under the MIT License. See LICENSE for details.
If you find this project useful, please consider giving it a ⭐ on GitHub!
Made with ❤️ by ethical0101


