A collaborative workspace for teams, schools and communities: a block editor, nested pages, inline databases, comments and chat, with real-time collaboration built in. Interface in English and French.
I wanted a tool where collaboration and notes actually stick around instead of getting lost in a chat scrollback somewhere. Rather than reinvent things Notion has already figured out (the editor, the database views, the page tree), I'm using those as a base and putting the effort into what's missing on top of that.
Editor (TipTap / ProseMirror)
- Text, headings, lists, to-dos, quotes, callouts with colour presets, toggles and toggle headings
- Columns you can resize (they stack on phones), tabs, tables with header toggles and saved widths, table of contents
- Code blocks with a searchable language picker, line numbers and wrap; code groups with one tab per language
- Math with KaTeX:
$…$inline,$$for a block - Images (upload, paste or drop, captions, alt text, lightbox), video (uploads, YouTube, Vimeo, Loom), audio, files with previews, web bookmarks, buttons, containers
/menu with recently used blocks and search in English and French;@mentions for people, pages and dates (@today,@friday,@in 3 days);:emoji with recents- Drag handle with a block menu: turn into, colours, move to another page, duplicate, delete
- Drag-box selection from the margin, multi-block drag, the
+button to insert below (Alt: above) - Paste Markdown and get blocks; export a page to Markdown, PDF or Word
- Keyboard shortcut sheet (
Ctrl+/)
Pages and workspaces
- Sidebar with nested pages, recents, pinned pages, trash; quick open (
Ctrl+P) and find in pages (Ctrl+Shift+F) - Workspaces and teamspaces with members, guests and roles; access enforced in Postgres (RLS)
- Templates, page covers and icons, version history
- Works offline: pages you've opened are kept on the device and sync when you're back online
Databases
- Inline databases with six views: table, board, gallery, list, calendar, timeline
- Filtering, sorting and grouping; column freezing, hiding and reordering
- Drag and drop across views: board columns, gallery cards, rescheduling on the calendar, moving and resizing timeline bars
Together
- Real-time collaborative editing with live cursors (Yjs + Hocuspocus)
- Comments and suggestions on pages and blocks, an inbox with notifications
- Chat rooms with replies, reactions, attachments and study sessions
Home and landing
- Home with recently visited pages, Learn guides and templates
- A public landing page with the real editor and a live collaboration demo
| Layer | Technology |
|---|---|
| Editor | TipTap 3, ProseMirror |
| Frontend | React 19, TypeScript, Vite |
| Styling | SCSS, CSS custom properties |
| Data | React Query (persisted for offline), Supabase JS |
| Backend | Supabase: Postgres, Auth, Row Level Security, Storage, Edge Functions |
| Real-time collaboration | Yjs, Hocuspocus |
| Languages | i18next (English, French) |
| Deployment | Vercel |
- Node.js 20.19 or newer (Vite 7 needs it)
- A Supabase project
- The Supabase CLI if you want to deploy the Edge Functions
npm installCreate .env.local at the repo root (it's git-ignored):
VITE_SUPABASE_URL=https://<your-project>.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=<your publishable (anon) key>
VITE_HOCUSPOCUS_URL=ws://localhost:1234In a new Supabase project, open the SQL Editor and run, in order:
supabase/migrations/000_baseline.sql: the whole schema (tables, functions, policies, storage buckets) and a little seed data.- Every migration numbered
043and higher, in order.
That's it: sign up in the app and the first account becomes the owner of the default workspace. Older migrations are kept in supabase/migrations/archive/ for history; don't run them.
New database changes go in a new numbered file after the highest one (see Data and security).
Two functions keep secrets and third-party calls off the browser:
| Function | What it does | Secret |
|---|---|---|
link-preview |
Title, description, image and favicon for web bookmarks | none |
pexel-search |
Cover image search | PEXELS_API_KEY |
supabase secrets set PEXELS_API_KEY=<your key>
supabase functions deploy link-preview
supabase functions deploy pexel-searchPages are Yjs documents served by Hocuspocus. It checks each connection against Supabase (the user's token and their access to the page) and keeps documents in a local SQLite file (folio-hocuspocus.sqlite).
cd hocuspocus-server
npm installCreate hocuspocus-server/.env:
SUPABASE_URL=https://<your-project>.supabase.co
SUPABASE_SERVICE_ROLE_KEY=<your service role key> # server only, never in the frontend
PORT=1234Then start it:
npm startIn another terminal, at the repo root:
npm run devThe app opens on Vite's dev server (http://localhost:5173). The script also starts two older local servers (server.js on port 3001, server/upload-server.js on port 3000). Data and uploads have moved to Supabase and the app no longer calls them; they'll be removed.
| Command | What it does |
|---|---|
npm run build |
Production build to dist/ |
npm run preview |
Serve the production build |
npm run lint |
ESLint |
npx tsc -p tsconfig.app.json --noEmit |
Type check |
- Every page is a Yjs document. The editor binds to it through TipTap's Collaboration extension, and Hocuspocus syncs it. A copy is kept in IndexedDB so pages you've opened work offline; edits made offline merge back when the connection returns.
- Database records live outside the ProseMirror document. They're pages with a
sourceIdand avaluesmap, resolved through a data source registry instead of stored inline, so the same record shows up consistently in every view. - A data bridge across node views. Cell and record node views are separate React roots and can't share context. The database's top-level view publishes its computed layout into editor storage, and each node view subscribes with
useSyncExternalStore. - One editor across navigation.
EditorProvidersits above the router, so moving between pages never remounts the editor shell. Loading states are skeletons drawn over the live editor. - Access control in the database. Page visibility is enforced with Row Level Security (
can_access_page); comment visibility follows page visibility, and the collaboration server asks the same rules before opening a document.
The folder layout and where new code goes are in docs/STRUCTURE.md.
The editor is in good shape. The next steps are the databases (closing gaps in properties and views), templates, publishing pages to the web, and the community gallery.
Folio is mostly built by Jule. Issues and pull requests are welcome: see CONTRIBUTING.md for how to set up, the conventions, and how to open a pull request.
Folio is free software, licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only).
In short: you can use, study, change and share Folio, including running it for other people. If you run a modified version that people use over a network, you have to make your modified source code available to them, under the same licence.
If you'd like to use Folio under different terms (for example, to build it into a closed-source product), contact the maintainer through GitHub.
Copyright © 2026 Jule and the Folio contributors.
