An open-source, self-hostable workspace app inspired by Notion. Built with Next.js.
- Rich text editor — Block-based editor with slash commands, headings, lists, todos, code blocks, and more
- Databases — Spreadsheet-style tables with sortable/filterable columns and inline editing
- Kanban boards — Drag-and-drop cards grouped by status
- Templates — Save and reuse page/database templates
- Team permissions — Admin, Editor, and Viewer roles with invite system
- Page sharing — Share pages with specific members or invite external guests via link
- Comments — Inline comments with quote highlighting and resolution
- Page history — Track changes with full edit history
- Search — Quick Cmd+K search across all pages
- Dark mode — Toggle between light and dark themes
- AI assist — Generate and edit text with OpenAI (optional)
- CLI — Full-featured command-line interface for scripting and bulk operations
- Next.js 16 (App Router, TypeScript)
- PostgreSQL via Supabase
- NextAuth.js for authentication (Google OAuth)
- BlockNote for the block editor
- Tailwind CSS + shadcn/ui
- TanStack React Query + Zustand
- Node.js 20+
- A Supabase project (free tier works)
- Google OAuth credentials (console.cloud.google.com)
# Install dependencies
npm install
# Copy env file and configure
cp .env.example .env
# Edit .env with your Supabase URL/key, Google OAuth credentials, and auth secret
# Run database migrations
# Copy each file from supabase/migrations/ into the Supabase SQL Editor and run in order
# Start development server
npm run devMotion uses Supabase (PostgreSQL) with SQL migrations. Run the migration files in order from supabase/migrations/ in the Supabase SQL Editor:
001_create_tables.sql— Core tables (users, workspaces, members, pages, blocks, databases, templates)002_add_private_pages.sql— Private pages support003_create_page_history.sql— Page history tracking004_create_notifications.sql— Notifications005_create_comments.sql— Comments006_add_row_archive.sql— Database row archiving007_create_page_shares.sql— Page sharing
After running migrations, create your first workspace directly in Supabase or via the CLI.
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
Yes | Your Supabase project URL |
SUPABASE_KEY |
Yes | Supabase service role key |
AUTH_SECRET |
Yes | Random secret for NextAuth.js session encryption |
NEXTAUTH_URL |
Yes | Your app URL (e.g. http://localhost:3000) |
GOOGLE_CLIENT_ID |
Yes | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Yes | Google OAuth client secret |
OPENAI_API_KEY |
No | Enables AI text generation/editing |
API_SECRET_KEY |
No | Bearer token for external /api/v1/* access |
DEFAULT_WORKSPACE_ID |
No | Auto-join new users to this workspace on sign-up |
To import your existing Notion content:
- Create a Notion integration at notion.so/my-integrations
- Share your Notion pages/databases with the integration
- Add your Notion API key to
.envasNOTION_API_KEY - Run:
npm run notion:migrate <workspaceId>
- Push this repo to GitHub
- Create a new Blueprint on Render, pointing to this repo
- Render will use
render.yamlto set up the web service - Set
NEXTAUTH_URLto your Render app URL
A full-featured command-line interface that mirrors the web UI. Useful for scripting, bulk operations, and working without a browser.
The CLI reads credentials from your .env file automatically. You just need to identify yourself:
# Option A: pass your email each time
npm run cli -- -u you@example.com workspace list
# Option B: set it once in your shell
export MOTION_USER_EMAIL=you@example.com
npm run cli -- workspace listYou can also pin a default workspace:
export MOTION_WORKSPACE_ID=your-workspace-uuidRun npm run cli -- --help or npm run cli -- <command> --help for full usage details.
npm run cli -- workspace listnpm run cli -- page list # list root pages
npm run cli -- page list -p <parentId> # list child pages
npm run cli -- page get <pageId> # page details, blocks, children
npm run cli -- page create -t "My Page" # create a page
npm run cli -- page create -t "Tasks" --type DATABASE --view-mode table
npm run cli -- page update <pageId> --title "New Title" --icon "🚀"
npm run cli -- page delete <pageId> # move to trash
npm run cli -- page delete <pageId> --permanent # permanent delete (admin)
npm run cli -- page restore <pageId> # restore from trash
npm run cli -- page lock <pageId> # lock page (admin)
npm run cli -- page unlock <pageId> # unlock page (admin)
npm run cli -- page favorite <pageId> # toggle favorite
npm run cli -- page move <pageId> --parent <id> # move under another page
npm run cli -- page search "query" # search by titlenpm run cli -- db props <pageId> # list properties (columns)
npm run cli -- db add-prop <pageId> -n "Priority" --type select \
--options '{"options":[{"value":"High","color":"red"}]}'
npm run cli -- db update-prop <pageId> --id <propId> -n "Renamed"
npm run cli -- db delete-prop <pageId> --id <propId>
npm run cli -- db rows <pageId> # list rows as a table
npm run cli -- db add-row <pageId> -c '[{"propertyId":"...","value":"Task 1"}]'
npm run cli -- db delete-row <pageId> --row <rowId>
npm run cli -- db set-cell <pageId> --property <propId> --row <rowId> --value "Done"npm run cli -- block list <pageId> # list blocks
npm run cli -- block save <pageId> -b '<json>' # replace all blocks (also accepts stdin)npm run cli -- template list
npm run cli -- template create -t "Meeting Notes" --from-page <pageId>
npm run cli -- template update --id <id> -t "Renamed"
npm run cli -- template delete --id <id>npm run cli -- member list
npm run cli -- member invite -e new@example.com -r EDITOR
npm run cli -- member update-role --id <memberId> -r ADMIN
npm run cli -- member remove --id <memberId>npm run cli -- share list <pageId>
npm run cli -- share add <pageId> --user-id <userId> --permission EDITOR
npm run cli -- share update <pageId> --share <shareId> --permission VIEWER
npm run cli -- share remove <pageId> --share <shareId>
npm run cli -- guest list <pageId>
npm run cli -- guest invite <pageId> -e external@example.com
npm run cli -- guest remove <pageId> --guest <guestId>npm run cli -- comment list <pageId>
npm run cli -- comment add <pageId> -c "Looks good!" -q "quoted text"
npm run cli -- comment resolve <pageId> --comment <commentId>npm run cli -- notif list
npm run cli -- notif read <notificationId>
npm run cli -- notif read-allnpm run cli -- profile get
npm run cli -- profile update -n "New Name"npm run cli -- trash list
npm run cli -- trash empty # permanent delete all (admin)npm run cli -- history list <pageId>
npm run cli -- history list <pageId> -l 50 -o 0 # paginatenpm run cli -- ai generate -p "Write a project brief for..."
npm run cli -- ai edit --text "Draft text here" -p "Make it more concise"Append --json to any command to get raw JSON instead of formatted tables:
npm run cli -- --json page list | jq '.[].title'
npm run cli -- --json db rows <pageId> | jq '.rows | length'| Variable | Description |
|---|---|
MOTION_USER_EMAIL |
Your email (avoids passing --user every time) |
MOTION_WORKSPACE_ID |
Default workspace ID (avoids passing --workspace) |
The CLI also reads SUPABASE_URL, SUPABASE_KEY, and OPENAI_API_KEY from .env for database access and AI features.