diff --git a/README.md b/README.md
index 32fd749b7..cd7dd5856 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,7 @@ Full end-to-end examples in Jupyter ready-to-run notebooks can be found in the [
* [Load data to a projected graph via graph construction](examples/load-data-via-graph-construction.ipynb)
* [Heterogeneous Node Classification with HashGNN and Autotuning](https://github.com/neo4j/graph-data-science-client/tree/main/examples/heterogeneous-node-classification-with-hashgnn.ipynb)
* [Perform inference using pre-trained KGE models](examples/kge-predict-transe-pyg-train.ipynb)
+* [Visualize GDS Projections](examples/visualize.ipynb)
## Documentation
diff --git a/doc/modules/ROOT/pages/tutorials/visualize.adoc b/doc/modules/ROOT/pages/tutorials/visualize.adoc
new file mode 100644
index 000000000..eb7722004
--- /dev/null
+++ b/doc/modules/ROOT/pages/tutorials/visualize.adoc
@@ -0,0 +1,266 @@
+// DO NOT EDIT - AsciiDoc file generated automatically
+
+= Visualizing GDS Projections
+
+
+https://colab.research.google.com/github/neo4j/graph-data-science-client/blob/main/examples/import-sample-export-gnn.ipynb[image:https://colab.research.google.com/assets/colab-badge.svg[Open
+In Colab]]
+
+
+This Jupyter notebook is hosted
+https://github.com/neo4j/graph-data-science-client/blob/main/examples/visualize-with-pyvis.ipynb[here]
+in the Neo4j Graph Data Science Client Github repository.
+
+The notebook exemplifies how to visualize a graph projection in the GDS
+Graph Catalog using the `graphdatascience`
+(https://neo4j.com/docs/graph-data-science-client/current/[docs]) and
+`pyvis` (https://pyvis.readthedocs.io/en/latest/index.html[docs])
+libraries.
+
+== Prerequisites
+
+Running this notebook requires a Neo4j server with GDS installed. We
+recommend using Neo4j Desktop with GDS, or AuraDS.
+
+Also required are of course the Python libraries `graphdatascience` and
+`pyvis`:
+
+[source, python, role=no-test]
+----
+%pip install graphdatascience pyvis
+----
+
+== Setup
+
+We start by importing our dependencies and setting up our GDS client
+connection to the database.
+
+[source, python, role=no-test]
+----
+from graphdatascience import GraphDataScience
+import os
+from pyvis.network import Network
+----
+
+[source, python, role=no-test]
+----
+# Get Neo4j DB URI, credentials and name from environment if applicable
+NEO4J_URI = os.environ.get("NEO4J_URI", "bolt://localhost:7687")
+NEO4J_AUTH = None
+NEO4J_DB = os.environ.get("NEO4J_DB", "neo4j")
+if os.environ.get("NEO4J_USER") and os.environ.get("NEO4J_PASSWORD"):
+ NEO4J_AUTH = (
+ os.environ.get("NEO4J_USER"),
+ os.environ.get("NEO4J_PASSWORD"),
+ )
+gds = GraphDataScience(NEO4J_URI, auth=NEO4J_AUTH, database=NEO4J_DB)
+----
+
+== Sampling Cora
+
+Next we use the
+https://neo4j.com/docs/graph-data-science-client/current/common-datasets/#_cora[built-in
+Cora loader] to get the data into GDS. The nodes in the Cora dataset is
+represented by academic papers, and the relationships connecting them
+are citations.
+
+We will then sample a smaller representative subgraph from it that is
+more suitable for visualization.
+
+[source, python, role=no-test]
+----
+G = gds.graph.load_cora()
+----
+
+Let’s make sure we constructed the correct graph.
+
+[source, python, role=no-test]
+----
+print(f"Metadata for our loaded Cora graph `G`: {G}")
+print(f"Node labels present in `G`: {G.node_labels()}")
+----
+
+
+Metadata for our loaded Cora graph `G`: Graph(name=cora, node_count=2708, relationship_count=5429)
+Node labels present in `G`: ['Paper']
+
+It’s looks correct! Now let’s go ahead and sample the graph.
+
+We use the random walk with restarts sampling algorithm to get a smaller
+graph that structurally represents the full graph. In this example we
+will use the algorithm’s default parameters, but check out
+https://neo4j.com/docs/graph-data-science/current/management-ops/graph-creation/sampling/rwr/[the
+algorithm’s docs] to see how you can for example specify the size of the
+subgraph, and choose which start node around which the subgraph will be
+sampled.
+
+[source, python, role=no-test]
+----
+G_sample, _ = gds.graph.sample.rwr("cora_sample", G, randomSeed=42, concurrency=1)
+----
+
+We should have somewhere around 0.15 * 2708 ~ 406 nodes in our sample.
+And let’s see how many relationships we got.
+
+[source, python, role=no-test]
+----
+print(f"Number of nodes in our sample: {G_sample.node_count()}")
+print(f"Number of relationships in our sample: {G_sample.relationship_count()}")
+----
+
+
+Number of nodes in our sample: 406
+Number of relationships in our sample: 532
+
+Let’s also compute
+https://neo4j.com/docs/graph-data-science/current/algorithms/page-rank/[PageRank]
+on our sample graph, in order to get an importance score that we call
+``rank'' for each node. It will be interesting for context when we
+visualize the graph.
+
+[source, python, role=no-test]
+----
+gds.pageRank.mutate(G_sample, mutateProperty="rank")
+----
+
+----
+mutateMillis 0
+nodePropertiesWritten 406
+ranIterations 20
+didConverge False
+centralityDistribution {'min': 0.14999961853027344, 'max': 2.27294921...
+postProcessingMillis 1
+preProcessingMillis 0
+computeMillis 7
+configuration {'mutateProperty': 'rank', 'jobId': '5ca450ff-...
+Name: 0, dtype: object
+----
+
+== Exporting the sampled Cora graph
+
+We can now export the topology and node properties of our sampled graph
+that we want to visualize.
+
+Let’s start by fetching the relationships.
+
+[source, python, role=no-test]
+----
+sample_topology_df = gds.graph.relationships.stream(G_sample)
+display(sample_topology_df)
+----
+
+[cols=",,,",options="header",]
+|===
+| |sourceNodeId |targetNodeId |relationshipType
+|0 |31336 |31349 |CITES
+|1 |31336 |686532 |CITES
+|2 |31336 |1129442 |CITES
+|3 |31349 |686532 |CITES
+|4 |31353 |31336 |CITES
+|... |... |... |...
+|527 |34961 |31043 |CITES
+|528 |34961 |22883 |CITES
+|529 |102879 |9513 |CITES
+|530 |102884 |9513 |CITES
+|531 |767763 |1136631 |CITES
+|===
+
+532 rows × 3 columns
+
+We get the right amount of rows, one for each expected relationship. So
+that looks good.
+
+Next we should fetch the node properties we are interested in. Each node
+will have a ``subject'' property which will be an integer 0,…,6 that
+indicates which of seven academic subjects the paper represented by the
+nodes belong to. We will also fetch the PageRank property ``rank'' that
+we computed above.
+
+[source, python, role=no-test]
+----
+sample_node_properties_df = gds.graph.nodeProperties.stream(
+ G_sample,
+ ["subject", "rank"],
+ separate_property_columns=True,
+)
+display(sample_node_properties_df)
+----
+
+[cols=",,,",options="header",]
+|===
+| |nodeId |rank |subject
+|0 |164 |0.245964 |4.0
+|1 |434 |0.158500 |2.0
+|2 |1694 |0.961240 |5.0
+|3 |1949 |0.224912 |6.0
+|4 |1952 |0.150000 |6.0
+|... |... |... |...
+|401 |1154103 |0.319498 |3.0
+|402 |1154124 |0.627706 |0.0
+|403 |1154169 |0.154784 |0.0
+|404 |1154251 |0.187675 |0.0
+|405 |1154276 |0.277500 |0.0
+|===
+
+406 rows × 3 columns
+
+Now that we have all the data we want to visualize, we can create a
+network with PyVis. We color each node according to its ``subject'', and
+size it according to its ``rank''.
+
+[source, python, role=no-test]
+----
+net = Network(notebook = True,
+cdn_resources="remote",
+ bgcolor = "#222222",
+ font_color = "white",
+ height = "750px", # Modify according to your screen size
+ width = "100%",
+)
+
+# Seven suitable light colors, one for each "subject"
+subject_to_color = ["#80cce9", "#fbd266", "#a9eebc", "#e53145", "#d2a6e2", "#f3f3f3", "#ff91af"]
+
+# Add all the nodes
+for _, node in sample_node_properties_df.iterrows():
+ net.add_node(int(node["nodeId"]), color=subject_to_color[int(node["subject"])], value=node["rank"])
+
+# Add all the relationships
+net.add_edges(zip(sample_topology_df["sourceNodeId"], sample_topology_df["targetNodeId"]))
+
+net.show("cora-sample.html")
+----
+
+
+ifdef::backend-html5[]
+++++
+include::ROOT:partial$/cora-sample.html[]
+++++
+endif::[]
+
+
+Unsurprisingly we can see that papers largely seem clustered by academic
+subject. We also note that some nodes appear larger in size, indicating
+that they have a higher centrality score according to PageRank.
+
+We can scroll over the graphic to zoom in/out, and ``click and drag''
+the background to navigate to different parts of the network. If we
+click on a node, it will be highlighted along with the relationships
+connected to it. And if we ``click and drag'' a node, we can move it.
+
+Additionally one could enable more sophisticated navigational features
+for searching and filtering by providing `select_menu = True` and
+`filter_menu = True` respectively to the PyVis `Network` constructor
+above. Check out the
+https://pyvis.readthedocs.io/en/latest/index.html[PyVis documentation]
+for this.
+
+== Cleanup
+
+We remove the Cora graphs from the GDS graph catalog to free up memory.
+
+[source, python, role=no-test]
+----
+_ = G_sample.drop()
+_ = G.drop()
+----
diff --git a/doc/modules/ROOT/partials/cora-sample.html b/doc/modules/ROOT/partials/cora-sample.html
new file mode 100644
index 000000000..0f2bc651e
--- /dev/null
+++ b/doc/modules/ROOT/partials/cora-sample.html
@@ -0,0 +1,444 @@
+
+