0
# Render Functions
1
2
Customizable render functions for controlling how editor content is displayed. These functions provide complete control over the visual representation of editor content and are essential for creating custom editor UI.
3
4
## Capabilities
5
6
### Element Rendering
7
8
#### renderElement
9
10
Custom function for rendering Slate elements as React components.
11
12
```typescript { .api }
13
/**
14
* Custom element renderer props passed to renderElement
15
*/
16
interface RenderElementProps {
17
/** React children to render inside the element */
18
children: any;
19
/** The Slate element being rendered */
20
element: Element;
21
/** Required DOM attributes for proper Slate functionality */
22
attributes: {
23
'data-slate-node': 'element';
24
'data-slate-inline'?: true;
25
'data-slate-void'?: true;
26
dir?: 'rtl';
27
ref: any;
28
};
29
}
30
31
/**
32
* Type for custom element render function
33
* @param props - Render element props
34
* @returns JSX element representing the rendered element
35
*/
36
type RenderElementFunction = (props: RenderElementProps) => JSX.Element;
37
```
38
39
**Usage Example:**
40
41
```typescript
42
import React from 'react';
43
import { Editable, RenderElementProps } from 'slate-react';
44
45
const renderElement = ({ attributes, children, element }: RenderElementProps) => {
46
switch (element.type) {
47
case 'paragraph':
48
return <p {...attributes}>{children}</p>;
49
case 'heading-one':
50
return <h1 {...attributes}>{children}</h1>;
51
case 'heading-two':
52
return <h2 {...attributes}>{children}</h2>;
53
case 'block-quote':
54
return <blockquote {...attributes}>{children}</blockquote>;
55
case 'list-item':
56
return <li {...attributes}>{children}</li>;
57
case 'bulleted-list':
58
return <ul {...attributes}>{children}</ul>;
59
case 'numbered-list':
60
return <ol {...attributes}>{children}</ol>;
61
case 'link':
62
return (
63
<a {...attributes} href={element.url}>
64
{children}
65
</a>
66
);
67
default:
68
return <div {...attributes}>{children}</div>;
69
}
70
};
71
72
const MyEditor = () => (
73
<Editable renderElement={renderElement} />
74
);
75
```
76
77
### Leaf Rendering
78
79
#### renderLeaf
80
81
Custom function for rendering Slate text leaves with formatting.
82
83
```typescript { .api }
84
/**
85
* Custom leaf renderer props passed to renderLeaf
86
*/
87
interface RenderLeafProps {
88
/** React children to render inside the leaf */
89
children: any;
90
/** The Slate leaf being rendered (same as text) */
91
leaf: Text;
92
/** The Slate text node being rendered */
93
text: Text;
94
/** Required DOM attributes for proper Slate functionality */
95
attributes: {
96
'data-slate-leaf': true;
97
ref: any;
98
};
99
/** Position information for the leaf (optional) */
100
leafPosition?: LeafPosition;
101
}
102
103
/**
104
* Type for custom leaf render function
105
* @param props - Render leaf props
106
* @returns JSX element representing the rendered leaf
107
*/
108
type RenderLeafFunction = (props: RenderLeafProps) => JSX.Element;
109
```
110
111
**Usage Example:**
112
113
```typescript
114
import React from 'react';
115
import { Editable, RenderLeafProps } from 'slate-react';
116
117
const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
118
if (leaf.bold) {
119
children = <strong>{children}</strong>;
120
}
121
122
if (leaf.italic) {
123
children = <em>{children}</em>;
124
}
125
126
if (leaf.underline) {
127
children = <u>{children}</u>;
128
}
129
130
if (leaf.strikethrough) {
131
children = <del>{children}</del>;
132
}
133
134
if (leaf.code) {
135
children = <code>{children}</code>;
136
}
137
138
if (leaf.highlight) {
139
children = (
140
<span style={{ backgroundColor: 'yellow' }}>
141
{children}
142
</span>
143
);
144
}
145
146
return <span {...attributes}>{children}</span>;
147
};
148
149
const MyEditor = () => (
150
<Editable renderLeaf={renderLeaf} />
151
);
152
```
153
154
### Text Rendering
155
156
#### renderText
157
158
Custom function for rendering individual text nodes. Rarely needed unless you need very specific text handling.
159
160
```typescript { .api }
161
/**
162
* Custom text renderer props passed to renderText
163
*/
164
interface RenderTextProps {
165
/** The Slate text node being rendered */
166
text: Text;
167
/** React children to render */
168
children: any;
169
/** Required DOM attributes for proper Slate functionality */
170
attributes: {
171
'data-slate-text': true;
172
ref: any;
173
};
174
}
175
176
/**
177
* Type for custom text render function
178
* @param props - Render text props
179
* @returns JSX element representing the rendered text
180
*/
181
type RenderTextFunction = (props: RenderTextProps) => JSX.Element;
182
```
183
184
**Usage Example:**
185
186
```typescript
187
import React from 'react';
188
import { Editable, RenderTextProps } from 'slate-react';
189
190
const renderText = ({ attributes, children, text }: RenderTextProps) => {
191
// Custom text processing - e.g., syntax highlighting
192
if (text.text.includes('TODO:')) {
193
return (
194
<span {...attributes} style={{ color: 'red', fontWeight: 'bold' }}>
195
{children}
196
</span>
197
);
198
}
199
200
return <span {...attributes}>{children}</span>;
201
};
202
203
const MyEditor = () => (
204
<Editable renderText={renderText} />
205
);
206
```
207
208
### Placeholder Rendering
209
210
#### renderPlaceholder
211
212
Custom function for rendering the editor placeholder when it's empty.
213
214
```typescript { .api }
215
/**
216
* Custom placeholder renderer props passed to renderPlaceholder
217
*/
218
interface RenderPlaceholderProps {
219
/** React children to render (the placeholder text) */
220
children: any;
221
/** Required DOM attributes for proper Slate functionality */
222
attributes: {
223
'data-slate-placeholder': boolean;
224
dir?: 'rtl';
225
contentEditable: boolean;
226
ref: React.RefCallback<any>;
227
style: React.CSSProperties;
228
};
229
}
230
231
/**
232
* Type for custom placeholder render function
233
* @param props - Render placeholder props
234
* @returns JSX element representing the rendered placeholder
235
*/
236
type RenderPlaceholderFunction = (props: RenderPlaceholderProps) => JSX.Element;
237
```
238
239
**Usage Example:**
240
241
```typescript
242
import React from 'react';
243
import { Editable, RenderPlaceholderProps } from 'slate-react';
244
245
const renderPlaceholder = ({ attributes, children }: RenderPlaceholderProps) => (
246
<div
247
{...attributes}
248
style={{
249
...attributes.style,
250
color: '#aaa',
251
fontStyle: 'italic',
252
fontSize: '14px'
253
}}
254
>
255
{children}
256
</div>
257
);
258
259
const MyEditor = () => (
260
<Editable
261
placeholder="Type something amazing..."
262
renderPlaceholder={renderPlaceholder}
263
/>
264
);
265
```
266
267
### Chunk Rendering (Performance)
268
269
#### renderChunk
270
271
Custom function for rendering chunks in performance-optimized scenarios with large documents.
272
273
```typescript { .api }
274
/**
275
* Custom chunk renderer props passed to renderChunk
276
*/
277
interface RenderChunkProps {
278
/** Whether this is the highest-level chunk */
279
highest: boolean;
280
/** Whether this is the lowest-level chunk */
281
lowest: boolean;
282
/** React children to render inside the chunk */
283
children: any;
284
/** Required DOM attributes for proper Slate functionality */
285
attributes: {
286
'data-slate-chunk': true;
287
ref: any;
288
};
289
}
290
291
/**
292
* Type for custom chunk render function
293
* @param props - Render chunk props
294
* @returns JSX element representing the rendered chunk
295
*/
296
type RenderChunkFunction = (props: RenderChunkProps) => JSX.Element;
297
```
298
299
**Usage Example:**
300
301
```typescript
302
import React from 'react';
303
import { Editable, RenderChunkProps } from 'slate-react';
304
305
const renderChunk = ({ attributes, children, highest, lowest }: RenderChunkProps) => {
306
// Apply different styling based on chunk level
307
const className = highest ? 'chunk-highest' : lowest ? 'chunk-lowest' : 'chunk-middle';
308
309
return (
310
<div {...attributes} className={className}>
311
{children}
312
</div>
313
);
314
};
315
316
const LargeDocumentEditor = () => (
317
<Editable renderChunk={renderChunk} />
318
);
319
```
320
321
## Advanced Rendering Patterns
322
323
### Conditional Rendering
324
325
Render different components based on element properties or editor state:
326
327
```typescript
328
import React from 'react';
329
import { useSelected, useFocused, RenderElementProps } from 'slate-react';
330
331
const ConditionalElement = ({ attributes, children, element }: RenderElementProps) => {
332
const selected = useSelected();
333
const focused = useFocused();
334
335
const isActive = selected && focused;
336
337
switch (element.type) {
338
case 'image':
339
return (
340
<div {...attributes}>
341
<img
342
src={element.url}
343
alt={element.alt}
344
style={{
345
border: isActive ? '2px solid blue' : 'none',
346
maxWidth: '100%'
347
}}
348
/>
349
{children}
350
</div>
351
);
352
case 'video':
353
return (
354
<div {...attributes}>
355
<video
356
src={element.url}
357
controls
358
style={{
359
border: isActive ? '2px solid blue' : 'none',
360
maxWidth: '100%'
361
}}
362
/>
363
{children}
364
</div>
365
);
366
default:
367
return <div {...attributes}>{children}</div>;
368
}
369
};
370
```
371
372
### Void Elements
373
374
Render void elements (elements that don't contain editable text):
375
376
```typescript
377
import React from 'react';
378
import { RenderElementProps } from 'slate-react';
379
380
const VoidElement = ({ attributes, children, element }: RenderElementProps) => {
381
switch (element.type) {
382
case 'image':
383
return (
384
<div {...attributes}>
385
<div contentEditable={false}>
386
<img src={element.url} alt={element.alt} />
387
</div>
388
{children}
389
</div>
390
);
391
case 'horizontal-rule':
392
return (
393
<div {...attributes}>
394
<div contentEditable={false}>
395
<hr />
396
</div>
397
{children}
398
</div>
399
);
400
default:
401
return <div {...attributes}>{children}</div>;
402
}
403
};
404
```
405
406
### Interactive Elements
407
408
Create interactive elements with event handlers:
409
410
```typescript
411
import React from 'react';
412
import { useSlateStatic, RenderElementProps } from 'slate-react';
413
import { Transforms } from 'slate';
414
415
const InteractiveElement = ({ attributes, children, element }: RenderElementProps) => {
416
const editor = useSlateStatic();
417
418
const handleButtonClick = () => {
419
const path = ReactEditor.findPath(editor, element);
420
Transforms.setNodes(
421
editor,
422
{ clicked: !element.clicked },
423
{ at: path }
424
);
425
};
426
427
if (element.type === 'button') {
428
return (
429
<div {...attributes}>
430
<button
431
contentEditable={false}
432
onClick={handleButtonClick}
433
style={{
434
backgroundColor: element.clicked ? 'green' : 'gray',
435
color: 'white',
436
border: 'none',
437
padding: '8px 16px',
438
cursor: 'pointer'
439
}}
440
>
441
{element.text || 'Click me'}
442
</button>
443
{children}
444
</div>
445
);
446
}
447
448
return <div {...attributes}>{children}</div>;
449
};
450
```
451
452
### Complex Leaf Formatting
453
454
Handle complex text formatting combinations:
455
456
```typescript
457
import React from 'react';
458
import { RenderLeafProps } from 'slate-react';
459
460
const ComplexLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
461
let element = children;
462
463
// Apply formatting in specific order
464
if (leaf.code) {
465
element = <code>{element}</code>;
466
}
467
468
if (leaf.bold) {
469
element = <strong>{element}</strong>;
470
}
471
472
if (leaf.italic) {
473
element = <em>{element}</em>;
474
}
475
476
if (leaf.underline) {
477
element = <u>{element}</u>;
478
}
479
480
if (leaf.strikethrough) {
481
element = <del>{element}</del>;
482
}
483
484
if (leaf.highlight) {
485
element = (
486
<span style={{ backgroundColor: leaf.highlightColor || 'yellow' }}>
487
{element}
488
</span>
489
);
490
}
491
492
if (leaf.fontSize) {
493
element = (
494
<span style={{ fontSize: `${leaf.fontSize}px` }}>
495
{element}
496
</span>
497
);
498
}
499
500
if (leaf.color) {
501
element = (
502
<span style={{ color: leaf.color }}>
503
{element}
504
</span>
505
);
506
}
507
508
return <span {...attributes}>{element}</span>;
509
};
510
```
511
512
## Render Function Best Practices
513
514
### Always Spread Attributes
515
516
Always spread the `attributes` prop on your root element:
517
518
```typescript
519
// ✅ Correct
520
const MyElement = ({ attributes, children }: RenderElementProps) => (
521
<div {...attributes}>{children}</div>
522
);
523
524
// ❌ Incorrect - missing attributes
525
const BadElement = ({ children }: RenderElementProps) => (
526
<div>{children}</div>
527
);
528
```
529
530
### Preserve Children
531
532
Always render the `children` prop:
533
534
```typescript
535
// ✅ Correct
536
const MyElement = ({ attributes, children }: RenderElementProps) => (
537
<div {...attributes}>
538
<h1>My Header</h1>
539
{children}
540
</div>
541
);
542
543
// ❌ Incorrect - missing children
544
const BadElement = ({ attributes }: RenderElementProps) => (
545
<div {...attributes}>
546
<h1>My Header</h1>
547
</div>
548
);
549
```
550
551
### Use contentEditable={false} for Non-Editable Areas
552
553
For void elements or interactive components:
554
555
```typescript
556
const ImageElement = ({ attributes, children, element }: RenderElementProps) => (
557
<div {...attributes}>
558
<div contentEditable={false}>
559
<img src={element.url} alt={element.alt} />
560
</div>
561
{children}
562
</div>
563
);
564
```