Docs
Realtime Monaco

Realtime Monaco

Real-time Monaco editor for collaborative applications

Installation

Folder structure

  • components
  • hooks
  • lib
    • supabase
1'use client'
2
3import { Editor } from '@monaco-editor/react'
4import { SupabasePersistenceOptions } from '@supabase-labs/y-supabase'
5import { Awareness } from 'y-protocols/awareness.js'
6
7import { useConnectOnMount } from '../hooks/use-connect-on-mount'
8
9type RealtimeMonacoProps = {
10  channel: string
11  language?: string
12  height?: string | number
13  className?: string
14  awareness?: boolean | Awareness
15  persistence?: boolean | SupabasePersistenceOptions
16  theme?: 'light' | 'dark'
17}
18
19const DEFAULT_HEIGHT = 550
20
21const RealtimeMonaco = ({
22  channel,
23  language = 'javascript',
24  height = DEFAULT_HEIGHT,
25  awareness = true,
26  persistence,
27  theme,
28  ...rest
29}: RealtimeMonacoProps) => {
30  const { connectOnMount } = useConnectOnMount({ channel, persistence, awareness })
31
32  return (
33    <Editor
34      height={height}
35      language={language}
36      theme={theme === 'dark' ? 'vs-dark' : 'light'}
37      onMount={connectOnMount}
38      {...rest}
39    />
40  )
41}
42
43export { RealtimeMonaco }

Introduction

The Realtime Monaco component provides a collaborative code editor powered by Monaco and Yjs. It uses @supabase-labs/y-supabase under the hood to sync document state across clients through Supabase Realtime.

Features

  • Real-time document synchronization via Supabase Realtime broadcast
  • Cursor and selection sharing between collaborators (awareness)
  • Optional persistence to Postgres so documents survive page reloads
  • Supports all Monaco languages and themes
  • Room-based isolation for scoped collaboration

How it works under the hood

The component creates a Yjs document and connects it to a Supabase Realtime channel using SupabaseProvider from @supabase-labs/y-supabase. A MonacoBinding from y-monaco bridges the Yjs document with the Monaco editor model, keeping them in sync.

When awareness is enabled, each user's cursor position and selection are broadcast to other clients in the same channel. Remote cursors are rendered with unique colors via dynamically injected CSS.

When persistence is enabled, the full Yjs document state is saved to a Postgres table so it can be restored when clients reconnect.

Usage

Basic usage

import { RealtimeMonaco } from '@/components/realtime-monaco'
 
export default function MonacoPage() {
  return <RealtimeMonaco channel="realtime-monaco-demo" language="typescript" />
}

With persistence

Enable persistence to save the editor content to your Supabase database. This requires a table to store the Yjs document state.

First, create the required table in your Supabase project:

create table yjs_documents (
  room text primary key,
  state text not null
);

Then pass persistence to the component:

import { RealtimeMonaco } from '@/components/realtime-monaco'
 
export default function MonacoPage() {
  return <RealtimeMonaco channel="realtime-monaco-demo" language="typescript" persistence />
}

You can also pass a SupabasePersistenceOptions object:

<RealtimeMonaco
  channel="realtime-monaco-demo"
  language="typescript"
  persistence={{
    table: 'yjs_documents',
    roomColumn: 'room',
    stateColumn: 'state',
    storeTimeout: 2000,
  }}
/>

Without awareness

By default, cursor and selection positions are shared between collaborators. To disable this:

<RealtimeMonaco channel="realtime-monaco-demo" awareness={false} />

Props

PropTypeDescription
channelstringUnique channel name used to sync editor content between collaborators in the same session.
language?stringMonaco language identifier (e.g. typescript, python). Defaults to javascript.
height?string | numberHeight of the editor container. Accepts a pixel number or CSS string (e.g. "100%"). Defaults to 550.
className?stringCSS class applied to the editor wrapper element.
awareness?boolean | AwarenessEnables cursor and selection sharing between users. Pass false to disable or a custom Awareness instance. Defaults to true.
persistence?boolean | SupabasePersistenceOptionsPersists editor content to Supabase so it survives page reloads. Pass true for defaults or an options object for fine-grained control.
theme?'light' | 'dark'Color theme for the editor. Maps to Monaco's light and vs-dark themes.

SupabasePersistenceOptions

OptionTypeDefaultDescription
tablestring'yjs_documents'Name of the Postgres table used to store documents.
schemastring'public'Schema where the table is located.
roomColumnstring'room'Column used as the document identifier.
stateColumnstring'state'Column used to store the binary Yjs state.
storeTimeoutnumber1000Debounce delay (ms) before persisting changes.

Further reading