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

component-extensions.mddocs/

0

# Component Extensions

1

2

Extensions and utilities for integrating React components as ProseMirror node views and managing the portal system.

3

4

## Capabilities

5

6

### React Component Extension

7

8

Main extension for integrating React components as node views in the editor.

9

10

```typescript { .api }

11

/**

12

* Extension for integrating React components as ProseMirror node views

13

*/

14

class ReactComponentExtension extends Extension<ReactComponentOptions> {

15

/** Extension name */

16

static readonly name = 'reactComponent';

17

18

/** Create node view for React components */

19

createNodeViews(): NodeViewMethod | {};

20

21

/** Create plugin for managing React components */

22

createPlugin(): ProsemirrorPlugin;

23

}

24

25

interface ReactComponentOptions {

26

/** Default component environment */

27

defaultEnvironment?: ReactComponentEnvironment;

28

/** Whether to use strict mode */

29

strict?: boolean;

30

/** Custom portal container */

31

portalContainer?: HTMLElement;

32

}

33

34

type ReactComponentEnvironment = 'ssr' | 'dom' | 'both';

35

```

36

37

**Usage Example:**

38

39

```typescript

40

import React from 'react';

41

import { ReactComponentExtension, useRemirror } from '@remirror/react';

42

43

// Custom node component

44

function CustomNode({ node, updateAttributes }) {

45

return (

46

<div className="custom-node">

47

<h3>{node.attrs.title}</h3>

48

<button onClick={() => updateAttributes({ title: 'Updated!' })}>

49

Update Title

50

</button>

51

</div>

52

);

53

}

54

55

function MyEditor() {

56

const { manager, state } = useRemirror({

57

extensions: () => [

58

new ReactComponentExtension({

59

defaultEnvironment: 'dom',

60

}),

61

// Your other extensions that use React components

62

],

63

});

64

65

return <Remirror manager={manager} initialContent={state} />;

66

}

67

```

68

69

### Portal System

70

71

Advanced React portal management for rendering components in specific locations within the editor.

72

73

```typescript { .api }

74

/**

75

* Container for managing React portals within the editor

76

*/

77

class PortalContainer {

78

/** Render a React component in a portal */

79

render(Component: ComponentType, props?: any, key?: string): string;

80

81

/** Remove a portal by key */

82

remove(key: string): void;

83

84

/** Update a portal with new props */

85

update(key: string, props: any): void;

86

87

/** Clear all portals */

88

clear(): void;

89

90

/** Get all mounted portals */

91

getPortals(): PortalMap;

92

}

93

94

/**

95

* Component that renders all active portals into the DOM

96

*/

97

interface RemirrorPortals extends React.Component<RemirrorPortalsProps> {}

98

99

interface RemirrorPortalsProps {

100

/** Portal container instance */

101

container?: PortalContainer;

102

/** Custom render function */

103

renderPortal?: (portal: MountedPortal) => React.ReactNode;

104

}

105

106

/**

107

* Hook to subscribe to portal container updates

108

* @param container - Portal container to subscribe to

109

* @returns Current portal map and update trigger

110

*/

111

function usePortals(container?: PortalContainer): {

112

portals: PortalMap;

113

updateCount: number;

114

};

115

```

116

117

**Usage Example:**

118

119

```typescript

120

import React, { useEffect } from 'react';

121

import {

122

usePortalContainer,

123

RemirrorPortals,

124

useRemirror,

125

ReactComponentExtension

126

} from '@remirror/react';

127

128

function FloatingComponent({ position }: { position: { x: number; y: number } }) {

129

return (

130

<div

131

style={{

132

position: 'absolute',

133

left: position.x,

134

top: position.y,

135

background: 'white',

136

border: '1px solid #ccc',

137

padding: '8px',

138

}}

139

>

140

Floating content!

141

</div>

142

);

143

}

144

145

function EditorWithPortals() {

146

const { manager, state } = useRemirror({

147

extensions: () => [new ReactComponentExtension()],

148

});

149

150

const portalContainer = usePortalContainer();

151

152

useEffect(() => {

153

// Render a floating component

154

const key = portalContainer.render(FloatingComponent, {

155

position: { x: 100, y: 100 }

156

});

157

158

// Clean up on unmount

159

return () => portalContainer.remove(key);

160

}, [portalContainer]);

161

162

return (

163

<Remirror manager={manager} initialContent={state}>

164

<RemirrorPortals />

165

</Remirror>

166

);

167

}

168

```

169

170

### Node View Creation

171

172

Utilities and interfaces for creating React-based node views.

173

174

```typescript { .api }

175

/**

176

* Props passed to React node view components

177

*/

178

interface NodeViewComponentProps {

179

/** The ProseMirror node */

180

node: ProsemirrorNode;

181

/** Node view instance */

182

view: EditorView;

183

/** Function to get current position */

184

getPos: () => number;

185

/** Node decorations */

186

decorations: readonly Decoration[];

187

/** Whether node is selected */

188

selected: boolean;

189

/** Update node attributes */

190

updateAttributes: (attrs: ProsemirrorAttributes) => void;

191

/** Forward ref to DOM element */

192

forwardRef?: React.Ref<HTMLElement>;

193

}

194

195

/**

196

* Properties for creating node views with React components

197

*/

198

interface CreateNodeViewProps {

199

/** Node name */

200

name: string;

201

/** React component to render */

202

component: ComponentType<NodeViewComponentProps>;

203

/** Environment where component should render */

204

environment?: ReactComponentEnvironment;

205

/** Whether content is editable */

206

contentEditable?: boolean;

207

/** Custom wrapper element */

208

wrapper?: string;

209

}

210

```

211

212

## Portal Types

213

214

```typescript { .api }

215

/**

216

* A mounted portal with render props and key

217

*/

218

interface MountedPortal {

219

/** Unique portal key */

220

key: string;

221

/** Component to render */

222

Component: ComponentType;

223

/** Props to pass to component */

224

props: any;

225

/** Portal container element */

226

container: HTMLElement;

227

}

228

229

/**

230

* Array of portal container/component tuples

231

*/

232

type PortalList = Array<[HTMLElement, ComponentType]>;

233

234

/**

235

* Map of HTML elements to mounted portals

236

*/

237

type PortalMap = Map<HTMLElement, MountedPortal[]>;

238

239

/**

240

* Props containing Component to render

241

*/

242

interface RenderProps {

243

/** Component to render in portal */

244

Component: ComponentType;

245

/** Props to pass to component */

246

props?: any;

247

}

248

249

/**

250

* Props for individual render methods

251

*/

252

interface SingleRenderMethodProps extends RenderProps {

253

/** Unique key for the render */

254

key?: string;

255

}

256

```

257

258

## Extension Integration

259

260

```typescript { .api }

261

/**

262

* Create a node extension that uses React components

263

* @param config - Configuration for the React node extension

264

* @returns Configured node extension

265

*/

266

function createReactNodeExtension<T extends ProsemirrorAttributes = {}>(

267

config: ReactNodeExtensionConfig<T>

268

): NodeExtension<ReactNodeOptions<T>>;

269

270

interface ReactNodeExtensionConfig<T extends ProsemirrorAttributes = {}> {

271

/** Extension name */

272

name: string;

273

/** React component for the node */

274

component: ComponentType<NodeViewComponentProps & { attrs: T }>;

275

/** Node schema */

276

schema: NodeSpec;

277

/** Default attributes */

278

defaults?: T;

279

/** Environment configuration */

280

environment?: ReactComponentEnvironment;

281

}

282

283

interface ReactNodeOptions<T extends ProsemirrorAttributes = {}>

284

extends ReactComponentOptions {

285

/** Node-specific options */

286

nodeOptions?: {

287

/** Default attributes */

288

defaults?: T;

289

/** Custom node view factory */

290

createNodeView?: (props: CreateNodeViewProps) => NodeView;

291

};

292

}

293

```

294

295

**Usage Example:**

296

297

```typescript

298

import React from 'react';

299

import { createReactNodeExtension, NodeViewComponentProps } from '@remirror/react';

300

301

// Define your React component

302

function ImageNode({ node, updateAttributes }: NodeViewComponentProps) {

303

const { src, alt, width } = node.attrs;

304

305

return (

306

<div className="image-node">

307

<img

308

src={src}

309

alt={alt}

310

width={width}

311

style={{ maxWidth: '100%' }}

312

/>

313

<div className="image-controls">

314

<input

315

type="range"

316

min="100"

317

max="800"

318

value={width}

319

onChange={(e) => updateAttributes({ width: parseInt(e.target.value) })}

320

/>

321

<span>{width}px</span>

322

</div>

323

</div>

324

);

325

}

326

327

// Create the extension

328

const ImageExtension = createReactNodeExtension({

329

name: 'image',

330

component: ImageNode,

331

schema: {

332

attrs: {

333

src: { default: null },

334

alt: { default: '' },

335

width: { default: 400 },

336

},

337

parseDOM: [{

338

tag: 'img',

339

getAttrs: (element) => ({

340

src: element.getAttribute('src'),

341

alt: element.getAttribute('alt'),

342

width: parseInt(element.getAttribute('width') || '400'),

343

}),

344

}],

345

toDOM: (node) => ['img', {

346

src: node.attrs.src,

347

alt: node.attrs.alt,

348

width: node.attrs.width,

349

}],

350

},

351

defaults: {

352

src: '',

353

alt: '',

354

width: 400,

355

},

356

});

357

```

358

359

## Environment Handling

360

361

```typescript { .api }

362

/**

363

* Utilities for handling different rendering environments

364

*/

365

interface EnvironmentHandler {

366

/** Check if running in browser */

367

isBrowser: () => boolean;

368

369

/** Check if running in Node.js/SSR */

370

isServer: () => boolean;

371

372

/** Get appropriate renderer for environment */

373

getRenderer: (environment: ReactComponentEnvironment) => ComponentType;

374

375

/** Handle environment-specific initialization */

376

initialize: (environment: ReactComponentEnvironment) => void;

377

}

378

379

/**

380

* HOC for environment-aware component rendering

381

* @param Component - Component to wrap

382

* @param environment - Target environment

383

* @returns Environment-aware component

384

*/

385

function withEnvironment<P extends {}>(

386

Component: ComponentType<P>,

387

environment: ReactComponentEnvironment

388

): ComponentType<P>;

389

```

390

391

## SSR Support

392

393

```typescript { .api }

394

/**

395

* Server-side rendering utilities for React components

396

*/

397

interface SSRRenderer {

398

/** Render component to string for SSR */

399

renderToString: (component: ReactElement) => string;

400

401

/** Render component to static markup */

402

renderToStaticMarkup: (component: ReactElement) => string;

403

404

/** Hydrate server-rendered content */

405

hydrate: (component: ReactElement, container: HTMLElement) => void;

406

}

407

408

/**

409

* Configuration for SSR-compatible node views

410

*/

411

interface SSRNodeViewConfig {

412

/** Whether to render on server */

413

serverRender: boolean;

414

415

/** Whether to hydrate on client */

416

clientHydrate: boolean;

417

418

/** Fallback content for non-JS environments */

419

fallback?: string | ComponentType;

420

}

421

```