A mobile app for your existing BlockNote + Hocuspocus instance.
Already running BlockNote with Hocuspocus on the web? This gives your users a native mobile app that connects to the same backend — real-time collaboration included.
Built with Expo (React Native). Point it at your API and Hocuspocus server, and you have a working app.
- Real-time collaborative editing with live cursors and presence avatars
- Connects to your existing Hocuspocus server (same Yjs documents as your web app)
- Document list with search and grid/list toggle
- In-document chat (synced via Yjs — no extra server needed)
- Pluggable document templates
- Light and dark theme
- Export to DOCX/PDF (if your backend supports it)
Clone the repo and point it at your existing backend:
git clone https://github.com/moritzWa/blocknote-app.git
cd blocknote-app
npm installEdit config.ts (or set environment variables):
export const API_BASE_URL = 'https://your-app.com/api'; // your document REST API
export const HOCUSPOCUS_URL = 'wss://your-app.com/ws'; // your Hocuspocus servernpx expo startIf you're starting fresh, you need two things:
- A REST API for document CRUD (see API contract below)
- A Hocuspocus server for real-time sync — as simple as:
import { Server } from "@hocuspocus/server";
Server.configure({ port: 1234 }).listen();BlockNote is a web editor — it can't run natively in React Native. This app uses a hybrid architecture to bridge the gap:
- Expo DOM components (
'use dom') run BlockNote inside a managed WebView - BridgedWebSocket proxies WebSocket connections through the native layer, bypassing WebView CORS restrictions
- Zustand bridge store synchronizes state (title, connection status, chat) between native UI and the DOM editor
Your mobile users edit the same Yjs documents as your web users. Everything stays in sync through Hocuspocus.
blocknote-app/
├── app/ Expo Router pages (document list, editor)
├── src/ Reusable editor library
│ ├── context/ EditorAdapter interface (platform abstraction)
│ ├── components/ BlockNoteEditor, PresenceAvatars
│ ├── hooks/ useCollaboration, useDocumentChat
│ ├── stores/ Document CRUD store
│ └── lib/ Templates, block utilities
├── components/
│ ├── dom/ EditorDOM — BlockNote in 'use dom' + BridgedWebSocket
│ └── native/ TopBar, ChatSidebar, GuestBanner
├── services/ API client, EditorAdapter implementation
├── stores/ App-level Zustand stores
└── theme/ Colors, typography, spacing
| Variable | Default | Description |
|---|---|---|
EXPO_PUBLIC_API_URL |
http://localhost:3000/api |
Your document REST API |
EXPO_PUBLIC_HOCUSPOCUS_URL |
ws://localhost:3000/ws |
Your Hocuspocus WebSocket server |
All backend communication goes through the EditorAdapter interface. The default implementation uses plain fetch with no auth. To add authentication, wrap it:
const authenticatedAdapter: EditorAdapter = {
...baseAdapter,
fetch: async (url, options) => {
const headers = new Headers(options?.headers);
headers.set('Authorization', `Bearer ${myToken}`);
return fetch(url, { ...options, headers });
},
};See services/editorAdapter.ts for the full implementation.
Your backend needs to implement these endpoints. If you're already running BlockNote with Hocuspocus, you likely have most of this.
| Method | Endpoint | Body | Response |
|---|---|---|---|
GET |
/docs |
— | Document[] |
POST |
/docs |
{ title } |
Document |
GET |
/docs/:id |
— | Document |
PUT |
/docs/:id |
{ title } |
Document |
DELETE |
/docs/:id |
— | 204 |
| Method | Endpoint | Body | Response |
|---|---|---|---|
GET |
/docs/:id/export/html |
— | text/html |
POST |
/exports/docx |
{ content, title } |
application/octet-stream |
POST |
/exports/pdf |
{ content, title } |
application/octet-stream |
interface Document {
id: string;
title: string;
created_at: string;
updated_at: string;
}Edit src/lib/templates.ts to match your app's document types:
{
id: 'proposal',
name: 'Project Proposal',
description: 'Template for project proposals',
icon: '📋',
defaultTitle: 'New Proposal',
content: '<h1>Project Proposal</h1><h2>Summary</h2><p></p>...',
}| Layer | Technology |
|---|---|
| Mobile framework | Expo 55, React Native 0.83, React 19 |
| Editor | BlockNote 0.47 (block-based rich text) |
| Collaboration | Yjs (CRDT) + Hocuspocus (WebSocket provider) |
| State | Zustand 5 |
| Editor UI | Mantine v8 (inside DOM component only) |
| Navigation | Expo Router |
- Fork the repo
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'feat: add my feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request
MIT — see LICENSE