Skip to main content
Read-only mode allows you to display Yoopta content without enabling editing. This is useful for previews, published content, comments, or any scenario where you want to show formatted content without modification.

Enabling Read-Only Mode

Set the readOnly option when creating the editor:
import { createYooptaEditor } from '@yoopta/editor';

const editor = createYooptaEditor({
  plugins: PLUGINS,
  marks: MARKS,
  readOnly: true,  // Enable read-only mode
});

Dynamic Read-Only State

Toggle between editable and read-only modes:
import { useState, useMemo } from 'react';
import { createYooptaEditor } from '@yoopta/editor';

function DynamicEditor() {
  const [isReadOnly, setIsReadOnly] = useState(false);
  
  const editor = useMemo(
    () => createYooptaEditor({
      plugins: PLUGINS,
      marks: MARKS,
      readOnly: isReadOnly,
    }),
    [isReadOnly]
  );
  
  return (
    <div>
      <button onClick={() => setIsReadOnly(!isReadOnly)}>
        {isReadOnly ? 'Enable Editing' : 'Disable Editing'}
      </button>
      <YooptaEditor editor={editor} />
    </div>
  );
}
Changing readOnly requires recreating the editor. For better performance, consider using two separate editor instances or conditionally rendering.

Read-Only Behavior

When read-only mode is enabled:
  • No text input or editing
  • No block insertion or deletion
  • No drag-and-drop
  • No context menus or floating toolbars
  • Selection is still allowed (for copying)
  • Links and interactive elements still work

Checking Read-Only State

Use the useYooptaReadOnly hook to check the current state:
import { useYooptaReadOnly } from '@yoopta/editor';

function EditorToolbar() {
  const isReadOnly = useYooptaReadOnly();
  
  if (isReadOnly) {
    return null; // Don't show toolbar in read-only mode
  }
  
  return (
    <div className="toolbar">
      {/* Toolbar buttons */}
    </div>
  );
}
Or access it directly from the editor:
const isReadOnly = editor.readOnly;

if (!isReadOnly) {
  // Perform editable-only operations
}

Read-Only Preview Example

Create a preview component:
import { useMemo } from 'react';
import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import type { YooptaContentValue } from '@yoopta/editor';

type PreviewProps = {
  content: YooptaContentValue;
};

function ContentPreview({ content }: PreviewProps) {
  const editor = useMemo(
    () => createYooptaEditor({
      plugins: PLUGINS,
      marks: MARKS,
      value: content,
      readOnly: true,
    }),
    [content]
  );
  
  return (
    <div className="content-preview">
      <YooptaEditor
        editor={editor}
        style={{ padding: '20px' }}
      />
    </div>
  );
}

Conditional UI Components

Hide editing UI in read-only mode:
import { useYooptaReadOnly } from '@yoopta/editor';

function Editor() {
  const isReadOnly = useYooptaReadOnly();
  
  return (
    <YooptaEditor editor={editor}>
      {!isReadOnly && (
        <>
          <YooptaToolbar />
          <YooptaSlashCommandMenu />
          <YooptaFloatingBlockActions />
        </>
      )}
    </YooptaEditor>
  );
}

Published Content View

1

Create read-only editor

const publishedEditor = createYooptaEditor({
  plugins: PLUGINS,
  marks: MARKS,
  value: publishedContent,
  readOnly: true,
});
2

Remove editing UI

<YooptaEditor
  editor={publishedEditor}
  // No toolbar or menus
/>
3

Style for reading

<div className="published-article">
  <YooptaEditor
    editor={publishedEditor}
    style={{
      maxWidth: '680px',
      margin: '0 auto',
      padding: '40px 20px',
    }}
  />
</div>

Split View: Edit + Preview

Show editable and read-only views side-by-side:
import { useMemo, useState } from 'react';
import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import type { YooptaContentValue } from '@yoopta/editor';

function SplitViewEditor() {
  const [content, setContent] = useState<YooptaContentValue>({});
  
  // Editable editor
  const editableEditor = useMemo(
    () => createYooptaEditor({
      plugins: PLUGINS,
      marks: MARKS,
      readOnly: false,
    }),
    []
  );
  
  // Preview editor
  const previewEditor = useMemo(
    () => createYooptaEditor({
      plugins: PLUGINS,
      marks: MARKS,
      value: content,
      readOnly: true,
    }),
    [content]
  );
  
  return (
    <div className="split-view">
      <div className="editor-pane">
        <h3>Editor</h3>
        <YooptaEditor
          editor={editableEditor}
          onChange={setContent}
        >
          <YooptaToolbar />
          <YooptaSlashCommandMenu />
        </YooptaEditor>
      </div>
      
      <div className="preview-pane">
        <h3>Preview</h3>
        <YooptaEditor editor={previewEditor} />
      </div>
    </div>
  );
}

Read-Only with Selection

Allow text selection for copying in read-only mode:
function ReadOnlyWithCopy() {
  const editor = useMemo(
    () => createYooptaEditor({
      plugins: PLUGINS,
      marks: MARKS,
      value: content,
      readOnly: true,
    }),
    [content]
  );
  
  const handleCopy = () => {
    const text = editor.getPlainText(editor.getEditorValue());
    navigator.clipboard.writeText(text);
  };
  
  return (
    <div>
      <button onClick={handleCopy}>Copy All</button>
      <YooptaEditor editor={editor} />
    </div>
  );
}

Custom Read-Only Styling

/* Style read-only editor differently */
.yoopta-editor[data-readonly="true"] {
  background-color: #f9fafb;
  cursor: default;
}

/* Hide block actions in read-only */
.yoopta-editor[data-readonly="true"] .yoopta-block-actions {
  display: none;
}

/* Style selected text */
.yoopta-editor[data-readonly="true"] ::selection {
  background-color: #e0f2fe;
}
Apply the attribute:
<div data-readonly={editor.readOnly}>
  <YooptaEditor editor={editor} />
</div>

Conditional Plugins

Exclude interactive plugins in read-only mode:
function createEditorWithMode(readOnly: boolean) {
  const basePlugins = [Paragraph, HeadingOne, HeadingTwo];
  
  const interactivePlugins = readOnly
    ? []
    : [Video, File, Embed]; // Only include in editable mode
  
  return createYooptaEditor({
    plugins: [...basePlugins, ...interactivePlugins],
    marks: MARKS,
    readOnly,
  });
}

Comment System Example

Use read-only editors for comments:
type Comment = {
  id: string;
  author: string;
  content: YooptaContentValue;
  timestamp: Date;
};

function CommentThread({ comments }: { comments: Comment[] }) {
  return (
    <div className="comment-thread">
      {comments.map((comment) => {
        const editor = createYooptaEditor({
          plugins: PLUGINS,
          marks: MARKS,
          value: comment.content,
          readOnly: true,
        });
        
        return (
          <div key={comment.id} className="comment">
            <div className="comment-header">
              <strong>{comment.author}</strong>
              <time>{comment.timestamp.toLocaleString()}</time>
            </div>
            <div className="comment-body">
              <YooptaEditor editor={editor} />
            </div>
          </div>
        );
      })}
    </div>
  );
}

Performance Optimization

Read-only editors can be optimized:
import { memo } from 'react';

const ReadOnlyContent = memo(({ content }: { content: YooptaContentValue }) => {
  const editor = useMemo(
    () => createYooptaEditor({
      plugins: PLUGINS,
      marks: MARKS,
      value: content,
      readOnly: true,
    }),
    [content]
  );
  
  return <YooptaEditor editor={editor} />;
});

Accessibility

Improve accessibility for read-only content:
<div
  role="article"
  aria-label="Published content"
  aria-readonly="true"
>
  <YooptaEditor editor={editor} />
</div>

Best Practices

Don’t show toolbars or menus in read-only mode:
const isReadOnly = useYooptaReadOnly();

return (
  <YooptaEditor editor={editor}>
    {!isReadOnly && <YooptaToolbar />}
  </YooptaEditor>
);
Make it clear when content is read-only:
{editor.readOnly && (
  <div className="read-only-badge">
    Read-only mode
  </div>
)}
Even in read-only mode, users should be able to select and copy text.
Memoize read-only editors to prevent unnecessary re-renders:
const editor = useMemo(
  () => createYooptaEditor({ readOnly: true, value }),
  [value]
);

Build docs developers (and LLMs) love