Overview of React Graph Viz
masterreact-force-graph components with a Neo4j database. It allows users to input Cypher queries via a textarea, executes them against the Neo4j database, and renders the resulting data as a graph visualization.repository·master·Indexed 43 days ago
https://github.com/jexp/neo4j-3d-force-graphA demonstration project for connecting Neo4j databases to React-based 2D and 3D force-directed graph visualizations. It features the CypherViz component for executing Cypher queries and rendering results using react-force-graph-2d and 3d-force-graph, including support for incremental loading, node coloring, and relationship weights.
react-force-graph components with a Neo4j database. It allows users to input Cypher queries via a textarea, executes them against the Neo4j database, and renders the resulting data as a graph visualization.yarn start command. The application will be available at http://localhost:3000. The development server supports hot reloading (the page reloads on edits) and will output linting errors to the browser console.yarn startThis project demonstrates how to use the Neo4j JavaScript driver to query graph data and render it using 3d-force-graph.
Important Driver Notes:
Number object. You must convert these to standard JavaScript integers using .toNumber() when accessing values (e.g., r.get('source').toNumber()).LIMIT clause), wrap them in neo4j.int() to ensure compatibility.// Example of converting Neo4j numbers to JS integers
const link = {
source: r.get('source').toNumber(),
target: r.get('target').toNumber()
};
// Example of wrapping an integer for a Neo4j query
const params = { limit: neo4j.int(5000) };yarn build command. This generates a build folder containing optimized, bundled React code ready for deployment.yarn buildThe CypherViz component expects the Cypher query results to be mapped into a specific JSON structure for the graph visualization.
To work correctly with the component's internal loadData logic, the Cypher query must return columns named source and target. The component then transforms these records into a data object with the following shape:
nodes: An array of objects, where each object contains a name property (e.g., { name: 'Joe' }).links: An array of objects containing source and target properties matching the names in the nodes array (e.g., { source: 'Joe', target: 'Jane' }).{
"nodes": [{ "name": "Joe" }, { "name": "Jane" }],
"links": [{ "source": "Joe", "target": "Jane" }]
}Instead of loading the entire dataset at once, you can iterate through the Neo4j driver result records and update the graph incrementally. This is useful for large datasets.
result.records.forEach(r => {
const { nodes, links } = Graph.graphData();
const link = { source: r.get('source').toNumber(), target: r.get('target').toNumber() };
Graph.graphData({
nodes: [...nodes, { id: link.source }, { id: link.target }],
links: [...links, link]
});
}); Use properties from your Neo4j data to drive the visual scale of the graph elements. For example, use a size property for nodes and a weight property for links.
Tip: When using weights for relationship thickness, consider using log(weight) in your Cypher query to prevent links from becoming excessively thick.
MATCH (n)-[r]->(m)
RETURN { id: id(n), label:head(labels(n)), caption:n.name, size:n.pagerank } as source,
{ id: id(m), label:head(labels(m)), caption:m.name, size:m.pagerank } as target,
{ weight:log(r.weight), type:type(r)} as rel
LIMIT $limitconst Graph = ForceGraph3D()(elem)
.graphData(gData)
.nodeAutoColorBy('label')
.nodeVal('size')
.linkAutoColorBy('type')
.linkWidth('weight')
.nodeLabel(node => `${node.label}: ${node.caption}`)
.onNodeHover(node => elem.style.cursor = node ? 'pointer' : null);To visualize community structures or flow, you can color both nodes and links by a cluster ID and use linkDirectionalParticles to represent relationship weights as moving particles.
MATCH (n)-[r]->(m)
RETURN { id: id(n), label:head(labels(n)), community:n.louvain, caption:n.name, size:n.pagerank } as source,
{ id: id(m), label:head(labels(m)), community:n.louvain, caption:m.name, size:m.pagerank } as target,
{ weight:r.weight, type:type(r), community:case when n.community < m.community then n.community else m.community end} as rel
LIMIT $limitconst Graph = ForceGraph3D()(elem)
.graphData(gData)
.nodeAutoColorBy('community')
.nodeVal('size')
.linkAutoColorBy('community')
.linkWidth(0)
.linkDirectionalParticles('weight') // number of particles
.linkDirectionalParticleSpeed(0.001) // slow down
.nodeLabel(node => `${node.label}: ${node.caption}`)
.onNodeHover(node => elem.style.cursor = node ? 'pointer' : null);You can enhance the visualization by automatically coloring nodes based on a property (like a label) and showing custom text captions when hovering over nodes.
MATCH (n)-->(m)
RETURN { id: id(n), label:head(labels(n)), caption:n.name } as source,
{ id: id(m), label:head(labels(m)), caption:m.name } as target
LIMIT $limitconst Graph = ForceGraph3D()(elem)
.graphData(gData)
.nodeAutoColorBy('label')
.nodeLabel(node => `${node.label}: ${node.caption}`)
.onNodeHover(node => elem.style.cursor = node ? 'pointer' : null);The fastest way to load data is to query only the IDs of nodes and relationships. This minimizes the payload size from Neo4j.
MATCH (n)-->(m) RETURN id(n) as source, id(m) as target LIMIT $limitForceGraph3D()(document.getElementById('3d-graph')).graphData(gData)The CypherViz component is a React component designed to visualize Neo4j graph data by executing Cypher queries. It requires a Neo4j driver instance passed via the driver prop.
Internally, it manages a Cypher query string and a graph data object. It provides a way to execute the query against a specific database and transform the resulting records into a node-link format compatible with react-force-graph-2d.
import CypherViz from './CypherViz';
// Usage: <CypherViz driver={driver}/>
// Note: The driver must be a valid Neo4j driver instance.