🚧 Nested Marks
Nested marks allow you to create rich, hierarchical text structures where marks can contain other marks. This enables complex formatting scenarios like markdown-style text, HTML-like tags, and multi-level mark structures.
Understanding Nesting
Section titled “Understanding Nesting”Flat vs Nested
Section titled “Flat vs Nested”Flat marks (__value__): Content is plain text, nested patterns are ignored.
markup: '@[__value__]'value: '@[Hello *world*]'// Result: One mark with value = "Hello *world*" (literal asterisks)Nested marks (__nested__): Content can contain other marks.
markup: '*__nested__*'value: '*Hello **world***'// Result: Italic mark containing "Hello " + bold mark "world"Key Differences
Section titled “Key Differences”| Feature | __value__ | __nested__ |
|---|---|---|
| Content Type | Plain text | Supports child marks |
| Parsing | No recursive parsing | Recursive parsing |
| Props | value string | children ReactNode + nested string |
| Use Case | Simple marks | Hierarchical structures |
Enabling Nested Marks
Section titled “Enabling Nested Marks”Use the __nested__ placeholder instead of __value__:
// ✅ Supports nestingconst NestedMarkup = '**__nested__**'
// ❌ Does not support nestingconst FlatMarkup = '**__value__**'Example: Markdown-style formatting
import {MarkedInput} from 'rc-marked-input'import {useState} from 'react'
const FormatMark = ({children, nested}) => { // For nested marks, children is ReactNode // For flat marks, nested is string return <span>{children || nested}</span>}
function MarkdownEditor() { const [value, setValue] = useState('This is **bold with *italic* inside**')
return ( <MarkedInput value={value} onChange={setValue} Mark={FormatMark} options={[ { markup: '**__nested__**', slotProps: { mark: ({children}) => ({ children, style: {fontWeight: 'bold'}, }), }, }, { markup: '*__nested__*', slotProps: { mark: ({children}) => ({ children, style: {fontStyle: 'italic'}, }), }, }, ]} /> )}Props for Nested Marks
Section titled “Props for Nested Marks”When using __nested__, your Mark component receives:
interface MarkProps { value?: string // undefined for nested marks meta?: string // Metadata (if __meta__ used) nested?: string // Raw nested content as string children?: ReactNode // Rendered nested children (use this!)}children vs nested
Section titled “children vs nested”| Prop | Type | Description | When to Use |
|---|---|---|---|
children | ReactNode | Rendered child marks | Recommended - for rendering |
nested | string | Raw text content | Edge cases - for processing raw text |
Example:
function NestedMark({children, nested}) { // ✅ Recommended: Use children return <strong>{children}</strong>
// ⚠️ Edge case: Use nested for raw text // return <strong>{nested?.toUpperCase()}</strong>}Simple Nesting Example
Section titled “Simple Nesting Example”import {MarkedInput} from 'rc-marked-input'import {useState} from 'react'
function SimpleMark({children, style}) { return <span style={style}>{children}</span>}
function SimpleNested() { const [value, setValue] = useState('Text with **bold and *italic* formatting**')
return ( <MarkedInput value={value} onChange={setValue} Mark={SimpleMark} options={[ { markup: '**__nested__**', slotProps: { mark: ({children}) => ({ children, style: {fontWeight: 'bold'}, }), }, }, { markup: '*__nested__*', slotProps: { mark: ({children}) => ({ children, style: {fontStyle: 'italic'}, }), }, }, ]} /> )}Output for '**bold *italic***':
<span style="font-weight: bold"> bold <span style="font-style: italic">italic</span></span>Two Values Pattern (HTML-like Tags)
Section titled “Two Values Pattern (HTML-like Tags)”ParserV2 supports two values patterns where opening and closing tags must match:
markup: '<__value__>__nested__</__value__>'How it works:
- Pattern contains exactly two
__value__placeholders - Both values must be identical to match
- Perfect for HTML/XML-like structures
HTML-like Tags Example
Section titled “HTML-like Tags Example”import {MarkedInput} from 'rc-marked-input'import {useState} from 'react'
function HtmlLikeMark({value, children, nested}) { // Use value as the HTML tag name const Tag = (value || 'span') as React.ElementType return <Tag>{children || nested}</Tag>}
function HtmlEditor() { const [value, setValue] = useState( '<div>Container with <mark>highlighted text</mark> and <b>bold with <i>italic</i></b></div>' )
return ( <MarkedInput value={value} onChange={setValue} Mark={HtmlLikeMark} options={[{markup: '<__value__>__nested__</__value__>'}]} /> )}Matching rules:
// ✅ Valid - tags match'<div>content</div>''<span>content</span>'
// ❌ Invalid - tags don't match'<div>content</span>' // Won't be recognized'<b>content</i>' // Won't be recognizedCustom Two Values Patterns
Section titled “Custom Two Values Patterns”// BBCode-stylemarkup: '[__value__]__nested__[/__value__]'// Matches: [b]text[/b], [i]text[/i]
// Template tagsmarkup: '{{__value__}}__nested__{{/__value__}}'// Matches: {{section}}content{{/section}}
// Custom bracketsmarkup: '<<__value__>>__nested__<</value__>>'// Matches: <<tag>>content<</tag>>Deep Nesting
Section titled “Deep Nesting”Marks can be nested to any depth:
'**bold with *italic with ~~strikethrough~~***'
// Renders as:<bold> bold with <italic> italic with <strikethrough>strikethrough</strikethrough> </italic></bold>Example: Multi-level formatting
function MultiLevelEditor() { const [value, setValue] = useState('Normal **bold *italic ~~strike !!highlight!!~~***')
return ( <MarkedInput value={value} onChange={setValue} Mark={({children}) => <span>{children}</span>} options={[ { markup: '**__nested__**', slotProps: { mark: ({children}) => ({ children, style: {fontWeight: 'bold'}, }), }, }, { markup: '*__nested__*', slotProps: { mark: ({children}) => ({ children, style: {fontStyle: 'italic'}, }), }, }, { markup: '~~__nested__~~', slotProps: { mark: ({children}) => ({ children, style: {textDecoration: 'line-through'}, }), }, }, { markup: '!!__nested__!!', slotProps: { mark: ({children}) => ({ children, style: {background: 'yellow'}, }), }, }, ]} /> )}Accessing Nesting Information
Section titled “Accessing Nesting Information”Use useMark() hook to access nesting details:
import {useMark} from 'rc-marked-input'
function NestedAwareMark({children}) { const {depth, hasChildren, parent, children: tokens} = useMark()
return ( <div style={{marginLeft: depth * 20, border: '1px solid #ccc'}}> <div> Depth: {depth} | Has children: {hasChildren ? 'Yes' : 'No'} | Parent: {parent?.value || 'None'} </div> <div>{children}</div> </div> )}Nesting Properties
Section titled “Nesting Properties”| Property | Type | Description |
|---|---|---|
depth | number | Nesting level (0 = root) |
hasChildren | boolean | Whether mark has nested children |
parent | MarkToken | undefined | Parent mark token |
children | Token[] | Array of child tokens |
Example: Collapsible nested structure
function CollapsibleMark({children}) { const {label, hasChildren, depth} = useMark() const [collapsed, setCollapsed] = useState(false)
if (!hasChildren) { return <span>{label}</span> }
return ( <div style={{marginLeft: depth * 20}}> <button onClick={() => setCollapsed(!collapsed)}>{collapsed ? '▶' : '▼'}</button> <strong>{label}</strong> {!collapsed && <div className="children">{children}</div>} </div> )}Mixed Nesting and Metadata
Section titled “Mixed Nesting and Metadata”Combine __nested__ with __meta__:
markup: '@[__nested__](__meta__)'Example: Colored nested text
function ColoredMark({children, meta}) { const colors = { red: '#ffebee', blue: '#e3f2fd', green: '#e8f5e9', }
return <span style={{background: colors[meta] || 'transparent'}}>{children}</span>}
// Usage;<MarkedInput value="@[Red text with **bold**](red) and @[Blue](blue)" Mark={ColoredMark} options={[ {markup: '@[__nested__](__meta__)'}, { markup: '**__nested__**', slotProps: { mark: ({children}) => ({ children, style: {fontWeight: 'bold'}, }), }, }, ]}/>Complete Examples
Section titled “Complete Examples”Example 1: Markdown Editor
Section titled “Example 1: Markdown Editor”import {MarkedInput} from 'rc-marked-input'import {useState} from 'react'
function MarkdownMark({children, nested}) { return <span>{children || nested}</span>}
function MarkdownEditor() { const [value, setValue] = useState('This is **bold**, this is *italic*, and this is **bold with *italic* inside**.')
return ( <MarkedInput value={value} onChange={setValue} Mark={MarkdownMark} options={[ { markup: '**__nested__**', slotProps: { mark: ({children}) => ({ children, style: {fontWeight: 'bold'}, }), }, }, { markup: '*__nested__*', slotProps: { mark: ({children}) => ({ children, style: {fontStyle: 'italic'}, }), }, }, ]} /> )}Example 2: HTML Tag Editor
Section titled “Example 2: HTML Tag Editor”function HtmlTagMark({value, children}) { // Map tag names to React components or HTML elements const tagMap: Record<string, React.ElementType> = { div: 'div', span: 'span', p: 'p', b: 'strong', i: 'em', mark: 'mark', code: 'code', }
const Tag = tagMap[value || 'span'] || 'span'
return <Tag>{children}</Tag>}
function HtmlEditor() { const [value, setValue] = useState('<div>Article with <mark>highlighted <b>bold</b> text</mark></div>')
return ( <MarkedInput value={value} onChange={setValue} Mark={HtmlTagMark} options={[{markup: '<__value__>__nested__</__value__>'}]} /> )}Example 3: Custom BBCode
Section titled “Example 3: Custom BBCode”function BBCodeMark({value, children}) { const styles: Record<string, React.CSSProperties> = { b: {fontWeight: 'bold'}, i: {fontStyle: 'italic'}, u: {textDecoration: 'underline'}, color: {color: 'red'}, size: {fontSize: '20px'}, }
return <span style={styles[value || '']}>{children}</span>}
function BBCodeEditor() { const [value, setValue] = useState('[b]Bold [i]and italic[/i][/b] with [color]red text[/color]')
return ( <MarkedInput value={value} onChange={setValue} Mark={BBCodeMark} options={[{markup: '[__value__]__nested__[/__value__]'}]} /> )}Performance Considerations
Section titled “Performance Considerations”Rendering Performance
Section titled “Rendering Performance”Nested marks create more React elements:
// Flat: 1 mark = 1 React element'@[simple]' → <Mark>simple</Mark>
// Nested: Multiple React elements'**bold *italic***' → <Mark><Mark>italic</Mark></Mark>Optimization tips:
- Memoize Mark component:
const Mark = React.memo(({children}) => <span>{children}</span>)- Limit nesting depth for large documents:
// Set reasonable max depth in parser config (if supported)maxDepth: 5- Use flat marks when possible:
// If you don't need nesting, use __value__markup: '@[__value__]' // Faster than __nested__Parsing Performance
Section titled “Parsing Performance”Deep nesting requires recursive parsing:
// Fast: 1 level'**bold**'
// Slower: 5 levels'**a *b ~~c __d !!e!!__~~***'Best practices:
- Avoid unnecessarily deep nesting (>5 levels)
- Use flat marks for simple cases
- Profile with React DevTools for large documents
Best Practices
Section titled “Best Practices”// Use children for rendering<span>{children}</span>
// Provide fallback for non-nested<span>{children || nested}</span>
// Memoize expensive Mark componentsconst Mark = React.memo(({ children }) => <span>{children}</span>)
// Type your component properlyfunction Mark({ children }: { children?: ReactNode }) { return <span>{children}</span>}❌ Don’t
Section titled “❌ Don’t”// Don't modify children directlyfunction Bad({children}) { return <span>{children.toUpperCase()}</span> // Error!}
// Don't use value with __nested__function Bad({value}) { return <span>{value}</span> // value is undefined!}
// Don't create infinite loopsmarkup: '**__nested__**'value: '**text **nested****' // Can cause issuesTypeScript Support
Section titled “TypeScript Support”Type your nested Mark components:
import type {ReactNode} from 'react'
interface NestedMarkProps { value?: string meta?: string nested?: string children?: ReactNode // Important for nested marks}
function TypedMark({children, nested}: NestedMarkProps) { return <span>{children || nested}</span>}For HTML-like tags:
interface HtmlMarkProps { value?: string // Tag name children?: ReactNode}
function HtmlMark({value, children}: HtmlMarkProps) { const Tag = (value || 'span') as React.ElementType return <Tag>{children}</Tag>}Key Takeaways:
- Use
__nested__placeholder for hierarchical structures - Render
childrenprop in your Mark component - Two values pattern (
<__value__>...</__value__>) for matching tags - Access nesting info with
useMark()hook - Optimize with
React.memofor better performance