Marks are text-level formatting attributes like bold, italic, underline, and highlight. Unlike blocks and elements which define structure, marks define the visual appearance of text content.
What are Marks?
Marks are properties attached to text nodes that control their formatting:
packages/core/editor/src/editor/types.ts
type SlateElementTextNode = {
text : string ;
bold ?: boolean ;
italic ?: boolean ;
underline ?: boolean ;
code ?: boolean ;
strike ?: boolean ;
highlight ?: any ;
};
Marks follow the Slate.js text formatting model. They’re stored on leaf text nodes rather than as wrapper elements.
Creating Marks
Marks are created using the createYooptaMark function:
packages/core/editor/src/marks/index.ts
import { createYooptaMark } from '@yoopta/editor' ;
const Bold = createYooptaMark ({
type: 'bold' ,
hotkey: 'mod+b' ,
render : ({ children , leaf }) => (
< strong >{ children } </ strong >
),
});
Mark Configuration
packages/core/editor/src/marks/index.ts
type YooptaMarkParams < TProps > = {
type : string ; // Mark identifier
hotkey ?: string ; // Keyboard shortcut
render : ( props : TProps ) => JSX . Element ; // Rendering function
};
Property Type Required Description typestringYes Unique mark identifier (e.g., ‘bold’, ‘italic’) hotkeystringNo Keyboard shortcut (e.g., ‘mod+b’ for Ctrl/Cmd+B) renderfunctionYes React component to render marked text
The mod key in hotkeys refers to Ctrl on Windows/Linux and Cmd on macOS.
Built-in Marks
Yoopta Editor provides common text marks through @yoopta/marks:
import { Bold , Italic , Underline , Strike , Code , Highlight } from '@yoopta/marks' ;
const MARKS = [ Bold , Italic , Underline , Strike , Code , Highlight ];
const editor = createYooptaEditor ({
plugins: PLUGINS ,
marks: MARKS ,
value: initialValue ,
});
Available Built-in Marks
Mark Hotkey Rendered As Usage Bold Cmd/Ctrl+B<strong>Make text bold Italic Cmd/Ctrl+I<em>Make text italic Underline Cmd/Ctrl+U<u>Underline text Strike Cmd/Ctrl+Shift+S<s>Strikethrough text Code Cmd/Ctrl+E<code>Inline code Highlight None <mark>Highlight text
Using Marks in Code
Creating Text with Marks
Use the editor.y.text() builder method:
// Plain text
editor . y . text ( 'Hello world' )
// Bold text
editor . y . text ( 'Bold text' , { bold: true })
// Multiple marks
editor . y . text ( 'Bold and italic' , {
bold: true ,
italic: true
})
// All formatting
editor . y . text ( 'Fully formatted' , {
bold: true ,
italic: true ,
underline: true ,
strike: true ,
code: true ,
highlight: {
color: '#000000' ,
backgroundColor: '#ffff00'
}
})
Creating Elements with Formatted Text
// Paragraph with mixed formatting
const paragraph = editor . y ( 'paragraph' , {
children: [
editor . y . text ( 'This is ' ),
editor . y . text ( 'bold' , { bold: true }),
editor . y . text ( ' and ' ),
editor . y . text ( 'italic' , { italic: true }),
editor . y . text ( ' text.' ),
]
});
// Insert into editor
editor . insertBlock ( 'Paragraph' , {
elements: paragraph ,
focus: true
});
Mark Operations
Updating Marks
Use the Marks namespace to apply formatting:
import { Marks } from '@yoopta/editor' ;
// Apply bold to current selection
Marks . update ( editor , {
type: 'bold' ,
value: true
});
// Apply highlight with color
Marks . update ( editor , {
type: 'highlight' ,
value: {
color: '#000000' ,
backgroundColor: '#ffff00'
}
});
// Apply to specific blocks
Marks . update ( editor , {
type: 'italic' ,
value: true ,
at: [ 0 , 1 , 2 ], // Block indices
});
Toggling Marks
Marks can be toggled on and off:
// Toggle bold (turns on if off, off if on)
Marks . toggle ( editor , { type: 'bold' });
// Toggle with specific value
Marks . toggle ( editor , {
type: 'highlight' ,
value: { backgroundColor: '#ffff00' }
});
Removing Marks
Set mark value to false or null:
// Remove bold
Marks . update ( editor , {
type: 'bold' ,
value: false
});
// Remove highlight
Marks . update ( editor , {
type: 'highlight' ,
value: null
});
Checking Active Marks
Access mark state through the editor’s formats:
// Get mark format interface
const boldFormat = editor . formats . bold ;
if ( boldFormat ) {
const isActive = boldFormat . isActive ();
const value = boldFormat . getValue ();
console . log ( 'Bold is active:' , isActive );
console . log ( 'Bold value:' , value );
}
Creating Custom Marks
You can create custom marks for specialized formatting:
import { createYooptaMark } from '@yoopta/editor' ;
// Custom superscript mark
const Superscript = createYooptaMark ({
type: 'superscript' ,
hotkey: 'mod+.' ,
render : ({ children }) => (
< sup style = {{ fontSize : '0.75em' , verticalAlign : 'super' }} >
{ children }
</ sup >
),
});
// Custom subscript mark
const Subscript = createYooptaMark ({
type: 'subscript' ,
hotkey: 'mod+,' ,
render : ({ children }) => (
< sub style = {{ fontSize : '0.75em' , verticalAlign : 'sub' }} >
{ children }
</ sub >
),
});
// Custom color mark
type ColorMarkProps = {
children : React . ReactNode ;
leaf : {
color ?: string ;
};
};
const Color = createYooptaMark < ColorMarkProps >({
type: 'color' ,
render : ({ children , leaf }) => (
< span style = {{ color : leaf . color }} >
{ children }
</ span >
),
});
Using Custom Marks
const MARKS = [
Bold ,
Italic ,
Underline ,
Superscript ,
Subscript ,
Color ,
];
const editor = createYooptaEditor ({
plugins: PLUGINS ,
marks: MARKS ,
value: initialValue ,
});
// Use in code
editor . y . text ( 'H' , { superscript: true });
editor . y . text ( '2' , { subscript: true });
editor . y . text ( 'O' );
// Apply via Marks API
Marks . update ( editor , {
type: 'color' ,
value: '#ff0000'
});
Mark Rendering
Marks receive props for rendering:
type YooptaMarkProps < K extends string , V > = {
children : React . ReactNode ; // The marked text
leaf : ExtendedLeaf < K , V >; // Leaf node with mark data
};
type ExtendedLeaf < K extends string , V > = {
text : string ;
[ markType ] : V ; // Mark-specific data
withPlaceholder ?: boolean ;
elementPlaceholder ?: string ;
};
Accessing Mark Data
const Highlight = createYooptaMark ({
type: 'highlight' ,
render : ({ children , leaf }) => {
const { highlight } = leaf ;
if ( ! highlight ) return children ;
return (
< mark
style = {{
backgroundColor : highlight . backgroundColor ,
color : highlight . color ,
}}
>
{ children }
</ mark >
);
},
});
Mark Composition
Multiple marks can be applied to the same text:
// Text with multiple marks
editor . y . text ( 'Important' , {
bold: true ,
italic: true ,
underline: true ,
highlight: {
backgroundColor: '#ffff00'
}
})
// Renders as:
// <strong><em><u><mark>Important</mark></u></em></strong>
Be careful with mark composition as too many nested marks can impact rendering performance. Limit to 3-4 marks per text node when possible.
Hotkeys
Hotkeys are automatically registered when marks are configured:
import { HOTKEYS } from '@yoopta/editor' ;
// Check if hotkey matches
const isBoldHotkey = HOTKEYS . isBold ( event );
const isItalicHotkey = HOTKEYS . isItalic ( event );
Available hotkey helpers:
HOTKEYS.isBold(event) - Cmd/Ctrl+B
HOTKEYS.isItalic(event) - Cmd/Ctrl+I
HOTKEYS.isUnderline(event) - Cmd/Ctrl+U
HOTKEYS.isStrike(event) - Cmd/Ctrl+Shift+S
HOTKEYS.isCode(event) - Cmd/Ctrl+E
The editor exposes a formats object with methods for each mark:
packages/core/editor/src/editor/types.ts
type TextFormat = {
type : string ;
hotkey ?: string ;
getValue : () => null | any ;
isActive : () => boolean ;
toggle : () => void ;
update : ( props ?: any ) => void ;
};
type YooptaFormats = Record < string , TextFormat >;
// Access format
const bold = editor . formats . bold ;
// Check if active
if ( bold . isActive ()) {
console . log ( 'Bold is active at current selection' );
}
// Get current value
const value = bold . getValue ();
// Toggle
bold . toggle ();
// Update with value
bold . update ( true );
Here’s an example of a formatting toolbar using marks:
import { useYooptaEditor } from '@yoopta/editor' ;
function FormattingToolbar () {
const editor = useYooptaEditor ();
const toggleMark = ( markType : string ) => {
const format = editor . formats [ markType ];
if ( format ) {
format . toggle ();
}
};
const isActive = ( markType : string ) => {
const format = editor . formats [ markType ];
return format ?. isActive () ?? false ;
};
return (
< div className = "toolbar" >
< button
onClick = { () => toggleMark ( 'bold' ) }
className = { isActive ( 'bold' ) ? 'active' : '' }
>
< strong > B </ strong >
</ button >
< button
onClick = { () => toggleMark ( 'italic' ) }
className = { isActive ( 'italic' ) ? 'active' : '' }
>
< em > I </ em >
</ button >
< button
onClick = { () => toggleMark ( 'underline' ) }
className = { isActive ( 'underline' ) ? 'active' : '' }
>
< u > U </ u >
</ button >
< button
onClick = { () => toggleMark ( 'strike' ) }
className = { isActive ( 'strike' ) ? 'active' : '' }
>
< s > S </ s >
</ button >
< button
onClick = { () => toggleMark ( 'code' ) }
className = { isActive ( 'code' ) ? 'active' : '' }
>
< code > { '</>' } </ code >
</ button >
</ div >
);
}
Mark Serialization
Marks are preserved when serializing to HTML or Markdown:
// HTML serialization
const html = editor . getHTML ( editor . children );
// <p>This is <strong>bold</strong> and <em>italic</em> text.</p>
// Markdown serialization
const markdown = editor . getMarkdown ( editor . children );
// This is **bold** and *italic* text.
Best Practices
1. Keep Mark Logic Simple
Marks should focus on visual formatting only, not behavior:
// Good: Simple formatting
const Bold = createYooptaMark ({
type: 'bold' ,
render : ({ children }) => < strong >{ children } </ strong > ,
});
// Bad: Adding behavior in marks
const Bold = createYooptaMark ({
type: 'bold' ,
render : ({ children }) => (
< strong onClick = { handleClick } > { children } </ strong >
),
});
2. Use Consistent Naming
Mark types should be lowercase and descriptive:
// Good
type : 'bold'
type : 'italic'
type : 'underline'
type : 'highlight'
// Bad
type : 'Bold'
type : 'ITALIC'
type : 'under_line'
3. Provide Meaningful Hotkeys
Use standard keyboard shortcuts when possible:
// Standard shortcuts
Bold : 'mod+b'
Italic : 'mod+i'
Underline : 'mod+u'
// Custom shortcuts should be intuitive
Superscript : 'mod+.'
Subscript : 'mod+,'
4. Handle Mark Conflicts
Some marks shouldn’t coexist (e.g., superscript and subscript):
const Superscript = createYooptaMark ({
type: 'superscript' ,
render : ({ children , leaf }) => {
// Don't render if subscript is active
if ( leaf . subscript ) return children ;
return < sup >{ children } </ sup > ;
},
});
Next Steps
Elements Learn about element structures
Plugins Build custom plugins with marks
Themes Style marks with themes
Editor Instance Back to editor API reference