CoTexts

Jazz provides two CoValue types for collaborative text editing, collectively referred to as "CoText" values:

  • CoPlainText for simple text editing without formatting
  • CoRichText for rich text with HTML-based formatting (extends CoPlainText)

Both types enable real-time collaborative editing of text content while maintaining consistency across multiple users.

Note: If you're looking for a quick way to add rich text editing to your app, check out jazz-richtext-prosemirror.

const const note: CoPlainTextnote = class CoPlainTextCoPlainText.
CoPlainText.create<CoPlainText>(this: CoValueClass<...>, text: string, options?: {
    owner: Account | Group;
} | Account | Group): CoPlainText
Create a new `CoPlainText` with the given text and owner. The owner (a Group or Account) determines access rights to the CoPlainText. The CoPlainText will immediately be persisted and synced to connected peers.
@example```ts const text = CoPlainText.create("Hello, world!", { owner: me }); ```@categoryCreation
create
("Meeting notes", { owner: Account | Groupowner: const me: Accountme });
// Update the text const note: CoPlainTextnote.CoPlainText.applyDiff(other: string): void
Apply text, modifying the text in place. Calculates the diff and applies it to the CoValue.
@categoryMutation
applyDiff
("Meeting notes for Tuesday");
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(const note: CoPlainTextnote.CoPlainText.toString(): string
Returns a string representation of a string.
toString
()); // "Meeting notes for Tuesday"

For a full example of CoTexts in action, see our Richtext example app, which shows plain text and rich text editing.

CoPlainText vs co.string

While co.string is perfect for simple text fields, CoPlainText is the right choice when you need:

  • Multiple users editing the same text simultaneously
  • Fine-grained control over text edits (inserting, deleting at specific positions)
  • Character-by-character collaboration
  • Efficient merging of concurrent changes

Both support real-time updates, but CoPlainText provides specialized tools for collaborative editing scenarios.

Creating CoText Values

CoText values are typically used as fields in your schemas:

class class ProfileProfile extends class CoMap
CoMaps are collaborative versions of plain objects, mapping string-like keys to values.
@categoryDescriptionDeclaration Declare your own CoMap schemas by subclassing `CoMap` and assigning field schemas with `co`. Optional `co.ref(...)` fields must be marked with `{ optional: true }`. ```ts import { co, CoMap } from "jazz-tools"; class Person extends CoMap { name = co.string; age = co.number; pet = co.ref(Animal); car = co.ref(Car, { optional: true }); } ```@categoryDescriptionContent You can access properties you declare on a `CoMap` (using `co`) as if they were normal properties on a plain object, using dot notation, `Object.keys()`, etc. ```ts person.name; person["age"]; person.age = 42; person.pet?.name; Object.keys(person); // => ["name", "age", "pet"] ```@categoryCoValues
CoMap
{
Profile.name: co<string>name =
const co: {
    string: co<string>;
    number: co<number>;
    boolean: co<boolean>;
    null: co<null>;
    Date: co<Date>;
    literal<T extends (string | number | boolean)[]>(..._lit: T): co<T[number]>;
    json<T extends CojsonInternalTypes.CoJsonValue<T>>(): co<T>;
    encoded<T>(arg: Encoder<T>): co<T>;
    ref: {
        ...;
    };
    items: ItemsSym;
    optional: {
        ref: <C extends CoValueClass>(arg: C | ((_raw: InstanceType<C>["_raw"]) => C)) => co<InstanceType<C> | null | undefined>;
        json<T extends CojsonInternalTypes.CoJsonValue<T>>(): co<T | undefined>;
        encoded<T>(arg: OptionalEncoder<T>): co<T | undefined>;
        string: co<string | undefined>;
        number: co<number | undefined>;
        boolean: co<boolean | undefined>;
        null: co<null | undefined>;
        Date: co<Date | undefined>;
        literal<T extends (string | number | boolean)[]>(..._lit: T): co<T[number] | undefined>;
    };
}
@categorySchema definition@categorySchema definition
co
.string: co<string>string;
Profile.bio: co<CoPlainText | null>bio =
const co: {
    string: co<string>;
    number: co<number>;
    boolean: co<boolean>;
    null: co<null>;
    Date: co<Date>;
    literal<T extends (string | number | boolean)[]>(..._lit: T): co<T[number]>;
    json<T extends CojsonInternalTypes.CoJsonValue<T>>(): co<T>;
    encoded<T>(arg: Encoder<T>): co<T>;
    ref: {
        ...;
    };
    items: ItemsSym;
    optional: {
        ref: <C extends CoValueClass>(arg: C | ((_raw: InstanceType<C>["_raw"]) => C)) => co<InstanceType<C> | null | undefined>;
        json<T extends CojsonInternalTypes.CoJsonValue<T>>(): co<T | undefined>;
        encoded<T>(arg: OptionalEncoder<T>): co<T | undefined>;
        string: co<string | undefined>;
        number: co<number | undefined>;
        boolean: co<boolean | undefined>;
        null: co<null | undefined>;
        Date: co<Date | undefined>;
        literal<T extends (string | number | boolean)[]>(..._lit: T): co<T[number] | undefined>;
    };
}
@categorySchema definition@categorySchema definition
co
.ref: <typeof CoPlainText>(arg: typeof CoPlainText | ((_raw: RawCoPlainText<JsonObject | null>) => typeof CoPlainText), options?: never) => co<...> (+1 overload)ref(class CoPlainTextCoPlainText); // Plain text field
Profile.description: co<CoRichText | null>description =
const co: {
    string: co<string>;
    number: co<number>;
    boolean: co<boolean>;
    null: co<null>;
    Date: co<Date>;
    literal<T extends (string | number | boolean)[]>(..._lit: T): co<T[number]>;
    json<T extends CojsonInternalTypes.CoJsonValue<T>>(): co<T>;
    encoded<T>(arg: Encoder<T>): co<T>;
    ref: {
        ...;
    };
    items: ItemsSym;
    optional: {
        ref: <C extends CoValueClass>(arg: C | ((_raw: InstanceType<C>["_raw"]) => C)) => co<InstanceType<C> | null | undefined>;
        json<T extends CojsonInternalTypes.CoJsonValue<T>>(): co<T | undefined>;
        encoded<T>(arg: OptionalEncoder<T>): co<T | undefined>;
        string: co<string | undefined>;
        number: co<number | undefined>;
        boolean: co<boolean | undefined>;
        null: co<null | undefined>;
        Date: co<Date | undefined>;
        literal<T extends (string | number | boolean)[]>(..._lit: T): co<T[number] | undefined>;
    };
}
@categorySchema definition@categorySchema definition
co
.ref: <typeof CoRichText>(arg: typeof CoRichText | ((_raw: RawCoPlainText<JsonObject | null>) => typeof CoRichText), options?: never) => co<...> (+1 overload)ref(class CoRichTextCoRichText); // Rich text with formatting
}

Create a CoText value with a simple string:

// Create plaintext with default ownership (current user)
const const note: CoPlainTextnote = class CoPlainTextCoPlainText.
CoPlainText.create<CoPlainText>(this: CoValueClass<CoPlainText>, text: string, options?: {
    owner: Account | Group;
} | Account | Group): CoPlainText
Create a new `CoPlainText` with the given text and owner. The owner (a Group or Account) determines access rights to the CoPlainText. The CoPlainText will immediately be persisted and synced to connected peers.
@example```ts const text = CoPlainText.create("Hello, world!", { owner: me }); ```@categoryCreation
create
("Meeting notes", { owner: Account | Groupowner: const me: Accountme });
// Create rich text with HTML content const const document: CoRichTextdocument = class CoRichTextCoRichText.
CoPlainText.create<CoRichText>(this: CoValueClass<CoRichText>, text: string, options?: {
    owner: Account | Group;
} | Account | Group): CoRichText
Create a new `CoPlainText` with the given text and owner. The owner (a Group or Account) determines access rights to the CoPlainText. The CoPlainText will immediately be persisted and synced to connected peers.
@example```ts const text = CoPlainText.create("Hello, world!", { owner: me }); ```@categoryCreation
create
("<p>Project <strong>overview</strong></p>",
{ owner: Account | Groupowner: const me: Accountme } );

Ownership

Like other CoValues, you can specify ownership when creating CoTexts.

// Create with shared ownership
const const teamGroup: GroupteamGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const teamGroup: GroupteamGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(const colleagueAccount: AccountcolleagueAccount, "writer"); const const teamNote: CoPlainTextteamNote = class CoPlainTextCoPlainText.
CoPlainText.create<CoPlainText>(this: CoValueClass<CoPlainText>, text: string, options?: {
    owner: Account | Group;
} | Account | Group): CoPlainText
Create a new `CoPlainText` with the given text and owner. The owner (a Group or Account) determines access rights to the CoPlainText. The CoPlainText will immediately be persisted and synced to connected peers.
@example```ts const text = CoPlainText.create("Hello, world!", { owner: me }); ```@categoryCreation
create
("Team updates", { owner: Group | Accountowner: const teamGroup: GroupteamGroup });

See Groups as permission scopes for more information on how to use groups to control access to CoText values.

Reading Text

CoText values work similarly to JavaScript strings:

// Get the text content
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(const note: CoPlainTextnote.CoPlainText.toString(): string
Returns a string representation of a string.
toString
()); // "Meeting notes"
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(`${const note: CoPlainTextnote}`); // "Meeting notes"
// Check the text length var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(const note: CoPlainTextnote.CoPlainText.length: number
Returns the length of a String object.
length
); // 14

Making Edits

Insert and delete text with intuitive methods:

// Insert text at a specific position
const note: CoPlainTextnote.CoPlainText.insertBefore(idx: number, text: string): voidinsertBefore(8, "weekly ");  // "Meeting weekly notes"

// Insert after a position
const note: CoPlainTextnote.CoPlainText.insertAfter(idx: number, text: string): voidinsertAfter(21, " for Monday");  // "Meeting weekly notes for Monday"

// Delete a range of text
const note: CoPlainTextnote.
CoPlainText.deleteRange(range: {
    from: number;
    to: number;
}): void
deleteRange
({ from: numberfrom: 8, to: numberto: 15 }); // "Meeting notes for Monday"
// Apply a diff to update the entire text const note: CoPlainTextnote.CoPlainText.applyDiff(other: string): void
Apply text, modifying the text in place. Calculates the diff and applies it to the CoValue.
@categoryMutation
applyDiff
("Team meeting notes for Tuesday");

Applying Diffs

Use applyDiff to efficiently update text with minimal changes:

// Original text: "Team status update"
const const minutes: CoPlainTextminutes = class CoPlainTextCoPlainText.
CoPlainText.create<CoPlainText>(this: CoValueClass<CoPlainText>, text: string, options?: {
    owner: Account | Group;
} | Account | Group): CoPlainText
Create a new `CoPlainText` with the given text and owner. The owner (a Group or Account) determines access rights to the CoPlainText. The CoPlainText will immediately be persisted and synced to connected peers.
@example```ts const text = CoPlainText.create("Hello, world!", { owner: me }); ```@categoryCreation
create
("Team status update", { owner: Account | Groupowner: const me: Accountme });
// Replace the entire text with a new version const minutes: CoPlainTextminutes.CoPlainText.applyDiff(other: string): void
Apply text, modifying the text in place. Calculates the diff and applies it to the CoValue.
@categoryMutation
applyDiff
("Weekly team status update for Project X");
// Make partial changes let let text: stringtext = const minutes: CoPlainTextminutes.CoPlainText.toString(): string
Returns a string representation of a string.
toString
();
let text: stringtext = let text: stringtext.String.replace(searchValue: string | RegExp, replaceValue: string): string (+3 overloads)
Replaces text in a string, using a regular expression or search string.
@paramsearchValue A string or regular expression to search for.@paramreplaceValue A string containing the text to replace. When the {@linkcode searchValue} is a `RegExp`, all matches are replaced if the `g` flag is set (or only those matches at the beginning, if the `y` flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
replace
("Weekly", "Monday");
const minutes: CoPlainTextminutes.CoPlainText.applyDiff(other: string): void
Apply text, modifying the text in place. Calculates the diff and applies it to the CoValue.
@categoryMutation
applyDiff
(let text: stringtext); // Efficiently updates only what changed

Perfect for handling user input in form controls:

const const note: CoPlainTextnote = class CoPlainTextCoPlainText.
CoPlainText.create<CoPlainText>(this: CoValueClass<CoPlainText>, text: string, options?: {
    owner: Account | Group;
} | Account | Group): CoPlainText
Create a new `CoPlainText` with the given text and owner. The owner (a Group or Account) determines access rights to the CoPlainText. The CoPlainText will immediately be persisted and synced to connected peers.
@example```ts const text = CoPlainText.create("Hello, world!", { owner: me }); ```@categoryCreation
create
("", { owner: Account | Groupowner: const me: Accountme });
// Create and set up the textarea const const textarea: HTMLTextAreaElementtextarea = var document: Document
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)
document
.Document.createElement<"textarea">(tagName: "textarea", options?: ElementCreationOptions): HTMLTextAreaElement (+2 overloads)
Creates an instance of the element for the specified tag.
@paramtagName The name of an element. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/createElement)
createElement
('textarea');
const textarea: HTMLTextAreaElementtextarea.HTMLTextAreaElement.value: string
Retrieves or sets the text in the entry field of the textArea element. [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLTextAreaElement/value)
value
= const note: CoPlainTextnote.CoPlainText.toString(): string
Returns a string representation of a string.
toString
();
// Add event listener for changes const textarea: HTMLTextAreaElementtextarea.HTMLTextAreaElement.addEventListener<"input">(type: "input", listener: (this: HTMLTextAreaElement, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)
Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)
addEventListener
('input', (e: Evente: Event) => {
const const target: HTMLTextAreaElementtarget = e: Evente.Event.target: EventTarget | null
Returns the object to which event is dispatched (its target). [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target)
target
as HTMLTextAreaElement;
// Efficiently update only what the user changed const note: CoPlainTextnote.CoPlainText.applyDiff(other: string): void
Apply text, modifying the text in place. Calculates the diff and applies it to the CoValue.
@categoryMutation
applyDiff
(const target: HTMLTextAreaElementtarget.HTMLTextAreaElement.value: string
Retrieves or sets the text in the entry field of the textArea element. [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLTextAreaElement/value)
value
);
}); // Add the textarea to the document var document: Document
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)
document
.Document.body: HTMLElement
Specifies the beginning and end of the document body. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/body)
body
.Node.appendChild<HTMLTextAreaElement>(node: HTMLTextAreaElement): HTMLTextAreaElement
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Node/appendChild)
appendChild
(const textarea: HTMLTextAreaElementtextarea);

Using Rich Text with ProseMirror

Jazz provides a dedicated plugin for integrating CoRichText with the popular ProseMirror editor. This plugin, jazz-richtext-prosemirror, enables bidirectional synchronization between your CoRichText instances and ProseMirror editors.

ProseMirror Plugin Features

  • Bidirectional Sync: Changes in the editor automatically update the CoRichText and vice versa
  • Real-time Collaboration: Multiple users can edit the same document simultaneously
  • HTML Conversion: Automatically converts between HTML (used by CoRichText) and ProseMirror's document model

Installation

pnpm add jazz-richtext-prosemirror \
  prosemirror-view \
  prosemirror-state \
  prosemirror-schema-basic

Integration

For use without a framework:

import { CoRichText } from "jazz-tools";
import { createJazzPlugin } from "jazz-richtext-prosemirror";
import { exampleSetup } from "prosemirror-example-setup";
import { schema } from "prosemirror-schema-basic";
import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";

function setupRichTextEditor(coRichText, container) {
  // Create the Jazz plugin for ProseMirror
  // Providing a CoRichText instance to the plugin to automatically sync changes
  const jazzPlugin = createJazzPlugin(coRichText); 
  
  // Set up ProseMirror with Jazz plugin
  const view = new EditorView(container, {
    state: EditorState.create({
      schema,
      plugins: [
        ...exampleSetup({ schema }),
        jazzPlugin,
      ],
    }),
  });
  
  // Return cleanup function
  return () => {
    view.destroy();
  };
}

// Usage
const document = CoRichText.create("<p>Initial content</p>", { owner: me });
const editorContainer = document.getElementById("editor");
const cleanup = setupRichTextEditor(document, editorContainer);

// Later when done with the editor
cleanup();