# Realtime Monaco

Real-time Monaco editor for collaborative applications

## Installation

Install this block:

```bash
npx shadcn@latest add @supabase/realtime-monaco-nextjs
```

## Folder structure

- `components/`
  - `realtime-monaco.tsx`
- `hooks/`
  - `use-connect-on-mount.ts`
- `lib/`
  - `supabase/`
    - `client.ts`
    - `middleware.ts`
    - `server.ts`

Full source: https://supabase.com/library/r/realtime-monaco-nextjs.json

## Introduction

The Realtime Monaco component provides a collaborative code editor powered by [Monaco](https://microsoft.github.io/monaco-editor/) and [Yjs](https://yjs.dev/). It uses [`@supabase-labs/y-supabase`](https://github.com/supabase-community/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](https://yjs.dev/) 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

```tsx
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:

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

Then pass `persistence` to the component:

```tsx
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:

```tsx
<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:

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

## Props

| Prop           | Type                                    | Description                                                                                                                                     |
| -------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel`      | `string`                                | Unique channel name used to sync editor content between collaborators in the same session.                                                      |
| `language?`    | `string`                                | Monaco [language identifier](https://code.visualstudio.com/docs/languages/identifiers) (e.g. `typescript`, `python`). Defaults to `javascript`. |
| `height?`      | `string \| number`                      | Height of the editor container. Accepts a pixel number or CSS string (e.g. `"100%"`). Defaults to `550`.                                        |
| `className?`   | `string`                                | CSS class applied to the editor wrapper element.                                                                                                |
| `awareness?`   | `boolean \| Awareness`                  | Enables cursor and selection sharing between users. Pass `false` to disable or a custom `Awareness` instance. Defaults to `true`.               |
| `persistence?` | `boolean \| SupabasePersistenceOptions` | Persists 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

| Option         | Type     | Default           | Description                                         |
| -------------- | -------- | ----------------- | --------------------------------------------------- |
| `table`        | `string` | `'yjs_documents'` | Name of the Postgres table used to store documents. |
| `schema`       | `string` | `'public'`        | Schema where the table is located.                  |
| `roomColumn`   | `string` | `'room'`          | Column used as the document identifier.             |
| `stateColumn`  | `string` | `'state'`         | Column used to store the binary Yjs state.          |
| `storeTimeout` | `number` | `1000`            | Debounce delay (ms) before persisting changes.      |

## Further reading

- [Realtime Broadcast](https://supabase.com/docs/guides/realtime/broadcast)
- [Realtime authorization](https://supabase.com/docs/guides/realtime/authorization)
- [`@supabase-labs/y-supabase`](https://github.com/supabase-community/y-supabase)
- [Yjs documentation](https://docs.yjs.dev/)
