Skip to content

How to embed the React editor ​

Mount CoralEditor in a React 18.3 or React 19 application and keep application state ownership outside the component.

Install the peers ​

sh
npm install @coral-viz/viz@0.2.5 @coral-viz/render@0.2.5 \
  @graph-ir/core@0.2.1 \
  @xyflow/react@^12 react@^19 react-dom@^19

Use matching React and React DOM majors. React 18 consumers use 18.3 or newer. Published core 0.2.1 supports GraphIR 2.0 and is pinned exactly by Coral's direct package dependencies. Release verification rechecks that registry identity rather than substituting a local build.

Mount the editor ​

  1. Import @xyflow/react/dist/style.css once in the host application.
  2. Give the editor container an explicit height and width.
  3. Pass a GraphIR 2.0 graph to CoralEditor.
  4. Use callbacks and the conversion helpers to move changes into host-owned state.

The React 18 and 19 consumer checks compile and hydrate this exact source:

tsx
// @ts-expect-error -- application bundlers resolve this CSS side-effect import.
import '@xyflow/react/dist/style.css';

import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import type { GraphIR } from '@coral-viz/viz';
import {
  CoralEditor,
  convertFlowToGraphEdges,
  convertFlowToGraphNodes,
  convertGraphToFlow,
} from '@coral-viz/viz/editor';

export const blankGraph: GraphIR = {
  version: '2.0.0',
  id: 'blank',
  nodes: [],
  edges: [],
};

const converterGraph: GraphIR = {
  version: '2.0.0',
  id: 'converter-check',
  nodes: [{ id: 'api', type: 'service', label: 'API', properties: { tier: 'web' } }],
  edges: [],
};

export function assertConverterRoundTrip(): void {
  const flow = convertGraphToFlow(converterGraph);
  const nodes = convertFlowToGraphNodes(flow.nodes, converterGraph.nodes);
  const edges = convertFlowToGraphEdges(flow.edges, converterGraph.edges);
  if (nodes[0]?.id !== 'api' || nodes[0]?.properties?.tier !== 'web' || edges.length !== 0) {
    throw new Error('GraphIR to React Flow conversion lost canonical fields');
  }
}

export function EditorExample(): React.JSX.Element {
  assertConverterRoundTrip();
  return (
    <div style={{ height: 480, width: '100%' }}>
      <CoralEditor
        graph={blankGraph}
        config={{ showControls: true, showMinimap: false }}
      />
    </div>
  );
}

export function hydrateEditor(container: Element): ReturnType<typeof hydrateRoot> {
  return hydrateRoot(container, <EditorExample />);
}

CoralEditor owns interactive React Flow state for the graph it receives. It doesn't load files, validate GraphIR, resolve notation profiles, run layout, persist changes, or choose the container size. The host owns those operations. Connections are disabled by default; enable them deliberately in config and apply the connection rules appropriate to the active notation.

The converters are the primary integration seam when a host already manages React Flow state. The round-trip check above preserves canonical node fields; current visual positions and handles own their corresponding GraphIR fields.