Skip to main content



This extension contains all the the components used to display causal graphs.

Getting Started

Add your first Causal Graph

Below we are going to show an example of how to add a CausalGraphViewer component.

from dara.components import CausalGraphViewer
from dara.core import ConfigurationBuilder, get_icon, ComponentInstance
from cai_causal_graph import CausalGraph

# Creates a CausalGraph
def default_causal_graph() -> CausalGraph:
# instantiate CausalGraph
cg = CausalGraph()

# Create edges
cg.add_edge('Age', 'Fraud')
cg.add_edge('Age', 'Marital Status Value=Married')
cg.add_edge('Age', 'Number of Children')
cg.add_edge('Authority Contacted', 'Fraud')
cg.add_edge('CPI', 'Salary')
cg.add_edge('Car Value', 'Fraud')
cg.add_edge('Claim Type Value=Liability', 'Total Claim')
cg.add_edge('Collision Type Value=Front', 'Claim Type Value=Liability')
cg.add_edge('Collision Type Value=Front', 'Total Claim')
cg.add_edge('Crime Rate', 'Fraud')
cg.add_edge('Education Level Value=Higher', 'Fraud')
cg.add_edge('Education Level Value=Higher', 'Occupation Value=Professional')
cg.add_edge('Gender Value=F', 'Salary')
cg.add_edge('Location Value=EC', 'Crime Rate')
cg.add_edge('Location Value=SE', 'Crime Rate')
cg.add_edge('Location Value=SW', 'Crime Rate')
cg.add_edge('Marital Status Value=Married', 'Fraud')
cg.add_edge('No-Claims Years', 'Fraud')
cg.add_edge('Number of Children', 'Fraud')
cg.add_edge('Occupation Value=Professional', 'Salary')
cg.add_edge('Previous Claims', 'Fraud')
cg.add_edge('Previous Claims Value', 'Fraud')
cg.add_edge('Salary', 'Car Value')
cg.add_edge('Salary', 'Fraud')
cg.add_edge('Total Claim', 'Fraud')
cg.add_edge('Unemployment Rate', 'Salary')
cg.add_edge('Years with License', 'Fraud')

return cg

# config
config = ConfigurationBuilder()

# adds causal graph viewer
def causal_graph_viewer_content() -> ComponentInstance:
return CausalGraphViewer(

config.add_page(name='CausalGraph', content=causal_graph_viewer_content(), icon=get_icon('diagram-project'))

CausalGraphViewer Example

CausalGraphViewer component

Customising Causal Graph Viewer

Rendering properties

The rendering of nodes and edges can be customised via the rendering_properties metadata. The following properties are available:


  • accepted: boolean - whether edge was accepted (used by resolver component)
  • color: string - edge color
  • description: string - description/note displayed in side panel
  • forced: boolean - whether edge was forced by constraints from domain knowledge
  • thickness: number - edge thickness; provided values are normalized and scaled across all edge thicknesses provided
  • tooltip: string | dict[string, string] - extra information to display in tooltip


  • color: string - node color, defaults to Theme.colors.background for latent nodes, Theme.colors.secondary for output nodes and to Theme.colors.blue4 for other nodes
  • highlight_color: string - color used for border and selected shadow, defaults to Theme.colors.primary
  • label: string - human-readable alternative label to display instead of the node name
  • label_color: string - node font color
  • label_size: string | number - node font size
  • latent: boolean - whether the node is latent; if not provided, computed based on available_inputs property
  • size: number - node radius in pixels
  • tooltip: string | dict[string, string] - extra information to display in tooltip
  • x: number - x position of node
  • y: number - y position of node

The metadata can be set in the following way:

from cai_causal_graph import CausalGraph

# Creates a CausalGraph
def graph_with_meta() -> CausalGraph:
cg = CausalGraph()

cg.add_node('client_age', meta={'rendering_properties': {
'color': 'red',
'label': 'Age',
'label_color': 'pink',
'label_size': 12,
'latent': False,
'size': 10,
'tooltip': 'Age of the client'

cg.add_edge('client_age', 'fraud', meta={'rendering_properties': {
'color': 'blue',
'description': 'This is a note which can also be edited via the UI',
'thickness': 1,
'tooltip': 'Connection between age and fraud'

return cg

Edge thickness

One of the rendering properties is thickness. This property is used to scale the displayed strength of the edges. When set on more than two edges, the values provided for thickness are normalized and scaled across all edges with a thickness provided.

from cai_causal_graph import CausalGraph

# Creates a CausalGraph
def graph_with_meta() -> CausalGraph:
cg = CausalGraph()

cg.add_edge('client_age', 'fraud', meta={'rendering_properties': {'thickness': 1}})
cg.add_edge('client_age', 'marital_status', meta={'rendering_properties': {'thickness': 2}})
cg.add_edge('client_age', 'number_of_children', meta={'rendering_properties': {'thickness': 3}})
cg.add_edge('client_age', 'occupation', meta={'rendering_properties': {'thickness': 4}})
cg.add_edge('client_age', 'salary', meta={'rendering_properties': {'thickness': 5}})
cg.add_edge('client_age', 'total_claim', meta={'rendering_properties': {'thickness': 6}})
cg.add_edge('client_age', 'unemployment_rate', meta={'rendering_properties': {'thickness': 7}})

return cg

CausalGraphViewer Example

CausalGraphViewer with edge thicknesses

Graph Layouts

The default layout you have seen in the previous examples is the FcoseLayout. Layouts are available as classes with customizable properties. You can read more about fCoSE and more available layouts in the GraphLayout and the layout docstrings.

You can set the layout by providing a layout instance as the graph_layout property of CausalGraphViewer:

from dara.components import CausalGraphViewer
from dara.components.graph_layout import PlanarLayout


CausalGraphViewer Example

CausalGraphViewer with PlanarLayout

In particular, you can provide your own layout by setting the x and y properties of each node in the rendering_properties metadata. Here's an example of using a custom layout using the networkx library and pygraphviz:

import networkx

cg = CausalGraph()
# ... add edges and nodes to cg

# Compute positions wih pygraphviz
cg_nx = cg.to_networkx()
nx_layout = networkx.nx_agraph.graphviz_layout(cg_nx)

# Update metadata to include the positions
scaling_factor = 5 # the layout needs to account for node sizes so we have to scale it
for node, positions in nx_layout.items():
x, y = positions
node = cg.get_node(node)
node.meta = {
'rendering_properties': {
'x': x * scaling_factor,
'y': y * scaling_factor

This can be used with the CustomLayout class to provide a fixed layout which can be restored upon pressing the Recalculate Layout button. When used with other layouts, the positions from metatdata are used directly and the specified layout algorithm is not ran on the first render. Further recalculations of the layout will use positions provided by the specified layout algorithm.

Note that third-party layout algorithms might not account for node sizes, so you might have to scale the positions accordingly unless they provide a way to specify the node size.

Editing the graph

While the CausalGraphViewer component can be used as a view-only component, it can also be used for interactive flows by passing editable=True.

When provided with a Variable containing a CausalGraph instance, it will update the graph in the Variable whenever the user makes changes to the graph.

When provided with a DerivedVariable, the graph will not updated it as DerivedVariables are read-only. You can either:

  • use the on_update callback to get notified of changes to the graph and save the updated state to another variable
from cai_causal_graph import CausalGraph
from dara.components import CausalGraphViewer
from dara.core import Variable, DerivedVariable

def compute_cg():
return CausalGraph(...)

# read-only computed causal graph
cg_dv = DerivedVariable(compute_cg, variables=[])

# writable variable to store the updated graph
cg_copy = Variable()

  • use the Variable.create_from_derived API to create a writable variable from a read-only variable. This copy will be in sync with the read-only variable until the user makes changes to the graph. You can read more about it in the interactivity documentation.
from cai_causal_graph import CausalGraph

def compute_cg():
return CausalGraph(...)

# read-only computed causal graph
cg_dv = DerivedVariable(compute_cg, variables=[])

# writable variable to store the updated graph
cg_copy = Variable.create_from_derived(cg_dv)



In order to interact with the causal graph, you can provide on_click_node and on_click_edge event handlers in order to trigger actions upon clicking on an edge or a node. The following example demonstrates how to use the on_click_node and on_click_edge event handlers to update a variable with the name of the clicked node or edge.

from dara.core import Variable, py_component, action
from dara.components import CausalGraphViewer, Stack, Text
from dara.components.graphs.graph_layout import PlanarLayout

from cai_causal_graph import CausalGraph

selected_node = Variable(None)
selected_edge = Variable(None)

causal_graph = CausalGraph()
causal_graph.add_edge('A', 'B')
causal_graph.add_edge('B', 'C')
causal_graph.add_edge('A', 'C')

async def resolver_on_click_node(ctx: action.Ctx):
value = ctx.input.get('identifier') if isinstance(ctx.input, dict) else None
await ctx.update(variable=selected_node, value=value)

async def resolver_on_click_edge(ctx: action.Ctx):
await ctx.update(variable=selected_edge, value=f"{ctx.input.get('source')} -> {ctx.input.get('destination')}")

def display(selected_node, selected_edge):
return Stack(
Text(f"Selected Node: {selected_node}"),
Text(f"Selected Edge: {selected_edge}"),

display(selected_node, selected_edge),