or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-hooks.mdcomponent-extensions.mdcontent-rendering.mdcore-integration.mdeditor-hooks.mdindex.mdtable-extensions.mdui-components.mdutilities.md

content-rendering.mddocs/

0

# Content Rendering

1

2

JSON-to-React rendering system for displaying editor content as React components with customizable handlers.

3

4

## Capabilities

5

6

### RemirrorRenderer Component

7

8

Main component for rendering Remirror JSON content as React components.

9

10

```typescript { .api }

11

/**

12

* Main recursive JSON-to-React renderer for displaying editor content

13

*/

14

interface RemirrorRenderer extends React.Component<RemirrorRendererProps> {}

15

16

interface RemirrorRendererProps {

17

/** Remirror JSON document to render */

18

json: RemirrorJSON;

19

/** Custom type mapping for nodes and marks */

20

typeMap?: Record<string, ComponentType>;

21

/** Custom mark mapping */

22

markMap?: MarkMap;

23

/** Additional props to pass to rendered components */

24

componentProps?: Record<string, any>;

25

/** Error boundary component */

26

errorBoundary?: ComponentType<{ error: Error; children: React.ReactNode }>;

27

}

28

29

interface RemirrorJSON {

30

/** Document type */

31

type: 'doc';

32

/** Document content nodes */

33

content?: RemirrorJSONNode[];

34

/** Document attributes */

35

attrs?: Record<string, any>;

36

}

37

38

interface RemirrorJSONNode {

39

/** Node type */

40

type: string;

41

/** Node content */

42

content?: RemirrorJSONNode[];

43

/** Node attributes */

44

attrs?: Record<string, any>;

45

/** Node marks */

46

marks?: RemirrorJSONMark[];

47

/** Text content (for text nodes) */

48

text?: string;

49

}

50

51

interface RemirrorJSONMark {

52

/** Mark type */

53

type: string;

54

/** Mark attributes */

55

attrs?: Record<string, any>;

56

}

57

```

58

59

**Usage Example:**

60

61

```typescript

62

import React from 'react';

63

import { RemirrorRenderer } from '@remirror/react';

64

65

// Sample Remirror JSON content

66

const sampleContent = {

67

type: 'doc',

68

content: [

69

{

70

type: 'paragraph',

71

content: [

72

{

73

type: 'text',

74

text: 'Hello ',

75

},

76

{

77

type: 'text',

78

text: 'world',

79

marks: [{ type: 'bold' }],

80

},

81

{

82

type: 'text',

83

text: '!',

84

},

85

],

86

},

87

],

88

};

89

90

function ContentViewer() {

91

return (

92

<div>

93

<h2>Rendered Content:</h2>

94

<RemirrorRenderer json={sampleContent} />

95

</div>

96

);

97

}

98

```

99

100

### Doc Component

101

102

Document root renderer component for complete documents.

103

104

```typescript { .api }

105

/**

106

* Document root renderer component for complete document rendering

107

*/

108

interface Doc extends React.Component<DocProps> {}

109

110

interface DocProps {

111

/** Remirror JSON document */

112

json: RemirrorJSON;

113

/** Document-level attributes */

114

attributes?: Record<string, any>;

115

/** Custom document wrapper component */

116

wrapper?: ComponentType<{ children: React.ReactNode }>;

117

}

118

```

119

120

### Content Handlers

121

122

Specialized components for rendering different types of content.

123

124

```typescript { .api }

125

/**

126

* Handler for rendering callout blocks

127

*/

128

interface Callout extends React.Component<CalloutProps> {}

129

130

interface CalloutProps {

131

/** Callout type/variant */

132

type?: 'info' | 'warning' | 'error' | 'success';

133

/** Callout title */

134

title?: string;

135

/** Callout content */

136

children: React.ReactNode;

137

/** Custom icon */

138

icon?: React.ReactNode;

139

}

140

141

/**

142

* Handler for rendering code blocks with syntax highlighting

143

*/

144

interface CodeBlock extends React.Component<CodeBlockProps> {}

145

146

interface CodeBlockProps {

147

/** Programming language for syntax highlighting */

148

language?: string;

149

/** Code content */

150

code: string;

151

/** Whether to show line numbers */

152

showLineNumbers?: boolean;

153

/** Custom theme for syntax highlighting */

154

theme?: string;

155

}

156

157

/**

158

* Handler for rendering heading elements

159

*/

160

interface Heading extends React.Component<HeadingProps> {}

161

162

interface HeadingProps {

163

/** Heading level (1-6) */

164

level: 1 | 2 | 3 | 4 | 5 | 6;

165

/** Heading content */

166

children: React.ReactNode;

167

/** Custom ID for anchor links */

168

id?: string;

169

/** Additional CSS classes */

170

className?: string;

171

}

172

173

/**

174

* Handler for rendering text content with marks

175

*/

176

interface TextHandler extends React.Component<TextHandlerProps> {}

177

178

interface TextHandlerProps {

179

/** Text content */

180

text: string;

181

/** Applied marks */

182

marks?: RemirrorJSONMark[];

183

/** Custom mark renderers */

184

markRenderers?: Record<string, ComponentType>;

185

}

186

```

187

188

**Usage Example:**

189

190

```typescript

191

import React from 'react';

192

import {

193

RemirrorRenderer,

194

Callout,

195

CodeBlock,

196

Heading

197

} from '@remirror/react';

198

199

function CustomContentRenderer() {

200

const customTypeMap = {

201

callout: ({ type, title, children }) => (

202

<Callout type={type} title={title}>

203

{children}

204

</Callout>

205

),

206

codeBlock: ({ language, code }) => (

207

<CodeBlock

208

language={language}

209

code={code}

210

showLineNumbers={true}

211

theme="dark"

212

/>

213

),

214

heading: ({ level, children, id }) => (

215

<Heading level={level} id={id}>

216

{children}

217

</Heading>

218

),

219

};

220

221

const content = {

222

type: 'doc',

223

content: [

224

{

225

type: 'heading',

226

attrs: { level: 1 },

227

content: [{ type: 'text', text: 'Welcome' }],

228

},

229

{

230

type: 'callout',

231

attrs: { type: 'info', title: 'Note' },

232

content: [

233

{

234

type: 'paragraph',

235

content: [{ type: 'text', text: 'This is important!' }],

236

},

237

],

238

},

239

],

240

};

241

242

return (

243

<RemirrorRenderer

244

json={content}

245

typeMap={customTypeMap}

246

/>

247

);

248

}

249

```

250

251

### Handler Factories

252

253

Factory functions for creating specialized content handlers.

254

255

```typescript { .api }

256

/**

257

* Factory for creating iframe handlers

258

* @param options - Iframe configuration options

259

* @returns Iframe handler component

260

*/

261

function createIFrameHandler(

262

options: IFrameHandlerOptions

263

): ComponentType<IFrameProps>;

264

265

interface IFrameHandlerOptions {

266

/** Allowed domains for iframes */

267

allowedDomains?: string[];

268

/** Default iframe attributes */

269

defaultAttributes?: Record<string, string>;

270

/** Security settings */

271

sandbox?: string[];

272

/** Error fallback component */

273

errorFallback?: ComponentType;

274

}

275

276

interface IFrameProps {

277

/** Iframe source URL */

278

src: string;

279

/** Iframe title */

280

title?: string;

281

/** Iframe dimensions */

282

width?: number | string;

283

height?: number | string;

284

/** Additional attributes */

285

attributes?: Record<string, string>;

286

}

287

288

/**

289

* Factory for creating link handlers

290

* @param options - Link configuration options

291

* @returns Link handler component

292

*/

293

function createLinkHandler(

294

options: LinkHandlerOptions

295

): ComponentType<LinkProps>;

296

297

interface LinkHandlerOptions {

298

/** Whether to open external links in new tab */

299

openExternalInNewTab?: boolean;

300

/** Custom link click handler */

301

onClick?: (href: string, event: React.MouseEvent) => void;

302

/** Link validation function */

303

validateLink?: (href: string) => boolean;

304

/** Custom link styling */

305

className?: string;

306

}

307

308

interface LinkProps {

309

/** Link destination */

310

href: string;

311

/** Link text content */

312

children: React.ReactNode;

313

/** Link title attribute */

314

title?: string;

315

/** Whether link is external */

316

external?: boolean;

317

}

318

```

319

320

**Usage Example:**

321

322

```typescript

323

import React from 'react';

324

import {

325

RemirrorRenderer,

326

createIFrameHandler,

327

createLinkHandler

328

} from '@remirror/react';

329

330

function SafeContentRenderer() {

331

const safeIFrameHandler = createIFrameHandler({

332

allowedDomains: ['youtube.com', 'vimeo.com'],

333

sandbox: ['allow-scripts', 'allow-same-origin'],

334

errorFallback: () => <div>Iframe not allowed</div>,

335

});

336

337

const customLinkHandler = createLinkHandler({

338

openExternalInNewTab: true,

339

onClick: (href, event) => {

340

console.log('Link clicked:', href);

341

},

342

validateLink: (href) => !href.includes('malicious'),

343

});

344

345

const typeMap = {

346

iframe: safeIFrameHandler,

347

link: customLinkHandler,

348

};

349

350

const content = {

351

type: 'doc',

352

content: [

353

{

354

type: 'paragraph',

355

content: [

356

{

357

type: 'text',

358

text: 'Check out this video: ',

359

},

360

{

361

type: 'link',

362

attrs: { href: 'https://youtube.com/watch?v=example' },

363

content: [{ type: 'text', text: 'YouTube Video' }],

364

},

365

],

366

},

367

],

368

};

369

370

return (

371

<RemirrorRenderer

372

json={content}

373

typeMap={typeMap}

374

/>

375

);

376

}

377

```

378

379

## Mark Mapping

380

381

```typescript { .api }

382

/**

383

* Mapping of marks to React components for rendering

384

*/

385

type MarkMap = Record<string, ComponentType<MarkProps>>;

386

387

interface MarkProps {

388

/** Mark attributes */

389

attrs?: Record<string, any>;

390

/** Content to wrap with the mark */

391

children: React.ReactNode;

392

/** Mark type name */

393

markType: string;

394

}

395

396

/**

397

* Create a custom mark renderer

398

* @param markType - The mark type to handle

399

* @param component - React component to render the mark

400

* @returns Mark renderer function

401

*/

402

function createMarkRenderer(

403

markType: string,

404

component: ComponentType<MarkProps>

405

): (props: MarkProps) => React.ReactNode;

406

```

407

408

## Rendering Context

409

410

```typescript { .api }

411

/**

412

* Context for renderer components to access parent renderer state

413

*/

414

interface RendererContext {

415

/** Current document being rendered */

416

document: RemirrorJSON;

417

/** Current rendering path */

418

path: number[];

419

/** Type mappings */

420

typeMap: Record<string, ComponentType>;

421

/** Mark mappings */

422

markMap: MarkMap;

423

/** Renderer options */

424

options: RendererOptions;

425

}

426

427

interface RendererOptions {

428

/** Whether to sanitize HTML attributes */

429

sanitizeAttributes?: boolean;

430

/** Maximum nesting depth */

431

maxDepth?: number;

432

/** Error handling mode */

433

errorMode?: 'throw' | 'render' | 'ignore';

434

}

435

436

/**

437

* Hook to access renderer context

438

* @returns Current renderer context

439

*/

440

function useRendererContext(): RendererContext;

441

```

442

443

## Performance Optimization

444

445

```typescript { .api }

446

/**

447

* Optimized renderer for large documents

448

*/

449

interface OptimizedRenderer extends React.Component<OptimizedRendererProps> {}

450

451

interface OptimizedRendererProps extends RemirrorRendererProps {

452

/** Whether to use virtual scrolling */

453

virtualScrolling?: boolean;

454

/** Chunk size for rendering */

455

chunkSize?: number;

456

/** Whether to lazy load content */

457

lazyLoad?: boolean;

458

/** Intersection observer options for lazy loading */

459

observerOptions?: IntersectionObserverInit;

460

}

461

462

/**

463

* Memoized content renderer for better performance

464

* @param props - Renderer props

465

* @returns Memoized renderer component

466

*/

467

const MemoizedRenderer: React.MemoExoticComponent<

468

React.ComponentType<RemirrorRendererProps>

469

>;

470

```

471

472

**Usage Example:**

473

474

```typescript

475

import React from 'react';

476

import {

477

OptimizedRenderer,

478

MemoizedRenderer,

479

useRendererContext

480

} from '@remirror/react';

481

482

function LargeDocumentRenderer({ json }) {

483

return (

484

<OptimizedRenderer

485

json={json}

486

virtualScrolling={true}

487

chunkSize={50}

488

lazyLoad={true}

489

observerOptions={{

490

threshold: 0.1,

491

rootMargin: '50px',

492

}}

493

/>

494

);

495

}

496

497

function CustomNodeRenderer({ children }) {

498

const context = useRendererContext();

499

const isNested = context.path.length > 3;

500

501

return (

502

<div className={isNested ? 'nested-content' : 'top-level-content'}>

503

{children}

504

</div>

505

);

506

}

507

```