An embedded React Flow-based graph viewer/editor for IntelliJ Platform plugins, built on top of JCEF (Chromium). This module provides:
- A Swing component (ReactFlowPanel) that hosts a lightweight web UI using React Flow
- A Java service bridge (ReactFlowService) to push nodes/edges, invoke actions (fitView, connect, etc.), and receive events/messages from the UI
This module is designed to be used as a library inside your IntelliJ plugin project. It is not a standalone plugin by itself.
You can find an example plugin using this library here
- Render nodes and edges using a bundled React Flow UI
- Programmatic API to set/update the graph from Java
- Event bridge from the web UI back to Java (graph changes, custom messages)
- Ships its own HTML resource (no external server), loads via JCEF
build.gradle.kts (of your plugin module)
repositories {
mavenCentral()
}
dependencies {
implementation("ca.nodeengine:intellijreactflow:1.0.1")
}This library uses JCEF and IntelliJ Platform APIs. Your plugin must target an IntelliJ Platform build that provides JCEF (2025.2+).
Create the panel, attach the service, and add it to your ToolWindow or dialog/panel.
import ca.nodeengine.intellijreactflow.ui.ReactFlowPanel;
import ca.nodeengine.intellijreactflow.services.ReactFlowService;
// ... inside your UI setup (e.g., ToolWindowFactory#createToolWindowContent)
ReactFlowPanel panel = new ReactFlowPanel();
ReactFlowService service = new ReactFlowService();
service.attachPanel(panel); // wires the bridge and JS calls
// Add the Swing component to your container
JComponent component = panel.getComponent();
contentPanel.add(component, BorderLayout.CENTER);
// Optionally, listen for events from the web UI
service.addListener(new ReactFlowService.Listener() {
@Override public void onReady() {
// The UI finished loading; you can now push nodes/edges
}
@Override public void onGraphChanged(String payloadJson) {
// React Flow emitted a graph change (positions, connections, etc.)
// payloadJson is a JSON string coming from JS
}
@Override public void onMessage(String type, String payloadJson) {
// Custom messages from the UI bridge
}
});You can send nodes/edges in bulk or incrementally.
// Define nodes
List<ReactFlowService.Node> nodes = new ArrayList<>();
nodes.add(new ReactFlowService.Node("n1", "First", 100, 150));
nodes.add(new ReactFlowService.Node("n2", "Second", 300, 150));
// Define edges
List<ReactFlowService.Edge> edges = new ArrayList<>();
edges.add(new ReactFlowService.Edge("e1", "n1", "n2", "connects", Map.of()));
// Send to UI
service.setGraph(nodes, edges);
// Alternatively set separately
// service.setNodes(nodes);
// service.setEdges(edges);
// Fit the view after setting the graph
service.fitView();You can also add a single node or connect nodes programmatically:
service.addNode("n3", "Third", 500, 100, Map.of("color", "#6c5ce7"));
service.connect("n2", "n3");Both nodes and edges accept a Map<String, Object> for arbitrary data. The bridge converts primitive types, strings, maps, and lists into JSON to send to the React Flow UI.
- Node(id, label, position(x, y), data)
- Edge(id, source, target, label, data)
On the JS side, this data is accessible as part of the node/edge objects.
ReactFlowPanel implements Disposable. In IntelliJ Platform, ensure you dispose the panel when the UI is torn down (e.g., when the ToolWindow content is removed) to properly release the JCEF browser.
public class MyToolWindowFactory implements ToolWindowFactory {
@Override
public void createToolWindowContent(Project project, ToolWindow toolWindow) {
ReactFlowPanel panel = new ReactFlowPanel();
ReactFlowService service = new ReactFlowService();
service.attachPanel(panel);
ContentFactory cf = ContentFactory.getInstance();
Content content = cf.createContent(panel.getComponent(), "Graph", false);
toolWindow.getContentManager().addContent(content);
// Wait for onReady or post a delayed runnable to set your graph
service.addListener(new ReactFlowService.Listener() {
@Override public void onReady() {
List<ReactFlowService.Node> nodes = List.of(
new ReactFlowService.Node("a", "Alpha", 100, 100),
new ReactFlowService.Node("b", "Beta", 300, 200)
);
List<ReactFlowService.Edge> edges = List.of(
new ReactFlowService.Edge("ab", "a", "b")
);
service.setGraph(nodes, edges);
service.fitView();
}
@Override public void onGraphChanged(String payloadJson) {}
@Override public void onMessage(String type, String payloadJson) {}
});
}
}Key entry points you may use:
- ReactFlowPanel
- JComponent getComponent()
- ReactFlowService
- void attachPanel(ReactFlowPanel panel)
- void setGraph(List, List)
- void setNodes(List)
- void setEdges(List)
- void addNode(String id, String label, int x, int y, Map<String, Object> data)
- void connect(String fromId, String toId)
- void fitView()
- void addListener(Listener l) / removeListener(Listener l)
- void requestGraph() // ask UI to send back current graph as JSON
- JCEF hosts the HTML found at: src/main/resources/intellijreactflow/index.html
- A JS bridge (JBCefJSQuery) connects Java to the page, enabling function calls and message passing
- Console messages from the web UI are forwarded to the IntelliJ log to assist with debugging
- The library currently exposes a curated subset of React Flow features via Java methods
- The web UI is bundled and loaded with resources; customizing the UI requires modifying the resource files
- Requires IntelliJ distributions that ship with JCEF enabled (most modern IDEs)
- Full ReactFlow API support
I'm but a single developer, so all contributions are welcome.