Skip to content

Movm/blocknote-app

Repository files navigation

BlockNote App

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.

Features

  • 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)

Quick Start

1. Configure your endpoints

Clone the repo and point it at your existing backend:

git clone https://github.com/moritzWa/blocknote-app.git
cd blocknote-app
npm install

Edit 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 server

2. Run the app

npx expo start

No existing backend?

If you're starting fresh, you need two things:

  1. A REST API for document CRUD (see API contract below)
  2. A Hocuspocus server for real-time sync — as simple as:
import { Server } from "@hocuspocus/server";
Server.configure({ port: 1234 }).listen();

How It Works

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

Configuration

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

EditorAdapter

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.

Backend API Contract

Your backend needs to implement these endpoints. If you're already running BlockNote with Hocuspocus, you likely have most of this.

Document CRUD (required)

Method Endpoint Body Response
GET /docs Document[]
POST /docs { title } Document
GET /docs/:id Document
PUT /docs/:id { title } Document
DELETE /docs/:id 204

Export (optional)

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

Document type

interface Document {
  id: string;
  title: string;
  created_at: string;
  updated_at: string;
}

Custom Templates

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>...',
}

Tech Stack

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

Contributing

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -m 'feat: add my feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

License

MIT — see LICENSE

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors