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
Create read-only editor
const publishedEditor = createYooptaEditor ({
plugins: PLUGINS ,
marks: MARKS ,
value: publishedContent ,
readOnly: true ,
});
Remove editing UI
< YooptaEditor
editor = { publishedEditor }
// No toolbar or menus
/>
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 ,
});
}
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 >
);
}
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 ]
);