# Rich Text Editing

LiveStore doesn't yet have built-in rich text primitives. It's currently recommended to pair LiveStore with a CRDT library (e.g. [Yjs](https://yjs.dev/), [Automerge](https://automerge.org/), or [Loro](https://loro.dev/)). LiveStore stores a reference (URL/ID) to the CRDT document in its event log. The CRDT library owns the text content and syncs it over its own transport. Both systems sync in parallel. LiveStore handles metadata while the CRDT library handles document bytes.

## Example: linking a note to an Automerge document


## `patterns/rich-text-editing/crdt-link.tsx`

```tsx filename="patterns/rich-text-editing/crdt-link.tsx"
import type { AutomergeUrl } from '@automerge/react'
import { updateText, useDocument } from '@automerge/react'
import type { ChangeEventHandler } from 'react'
import { useCallback } from 'react'

import { Events, Schema, State, type Store } from '@livestore/livestore'

declare const store: Store

type NoteDoc = { body: string }

// ---cut---

export const note = State.SQLite.table({
  name: 'note',
  columns: {
    id: State.SQLite.text({ primaryKey: true }),
    title: State.SQLite.text({ default: '' }),
    crdtDocUrl: State.SQLite.text({ nullable: false, default: '' }),
  },
})

export type Note = typeof note.Type
export const tables = { note }

export const events = {
  createNote: Events.synced({
    name: 'v1.CreateNote',
    schema: Schema.Struct({
      noteId: Schema.String,
      title: Schema.String,
      crdtDocUrl: Schema.String,
    }),
  }),
}

export const getNoteCrdtRef = (noteId: string): string | undefined => {
  return store.query(tables.note.select('crdtDocUrl').where({ id: noteId }))[0]
}

const Editor = ({ noteCrdtUrl }: { noteCrdtUrl: string }) => {
  const docUrl = noteCrdtUrl as AutomergeUrl
  const [doc, changeDoc] = useDocument<NoteDoc>(docUrl, { suspense: true })

  const handleChange: ChangeEventHandler<HTMLTextAreaElement> = useCallback(
    (event) => {
      changeDoc((draft: NoteDoc) => {
        updateText(draft, ['body'], event.target.value)
      })
    },
    [changeDoc],
  )

  return <textarea value={doc?.body ?? ''} onChange={handleChange} />
}

export const RichTextNoteEditor = ({ noteId }: { noteId: string }) => {
  const noteCrdtUrl = getNoteCrdtRef(noteId)
  if (noteCrdtUrl == null) return null

  return <Editor noteCrdtUrl={noteCrdtUrl} />
}
```

