Consuming Variables
In FlowGram, when a node wants to use variables from preceding nodes, it needs to consume those variables.
Reading Tips
- We recommend finishing Output Variables first so you know how variables are produced; this guide is the second stop focused on “how to access them.”
- To quickly preview the variable selector experience, try VariableSelector first; continue with the API sections when you need code-level access to variable lists.
- All examples assume the node scope. When private or global scopes show up, refer to Core Concepts – Variables in the Canvas for additional context.
VariableSelector
To make it easier for you to integrate variable selection functionality into your applications, the official materials provide the VariableSelector component.
See documentation: VariableSelector
Getting Accessible Variable Tree
In canvas nodes, we often need to get variables available in the current scope and display them in a tree structure for users to select and operate.
Common Needs at a Glance
- Just list variables →
useAvailableVariables
- Need drill-down for objects/arrays →
ASTMatch + recursive rendering
- Need precise subscriptions → go straight to
scope.available
useAvailableVariables
useAvailableVariables is a lightweight Hook that directly returns an array of variables available in the current scope (VariableDeclaration[]).
use-variable-tree.tsx
import {
type BaseVariableField,
useAvailableVariables,
} from '@flowgram.ai/fixed-layout-editor';
// .... In React component or Hook
const availableVariables = useAvailableVariables();
const renderVariable = (variable: BaseVariableField) => {
// You can render each variable according to your needs here
// ....
}
return availableVariables.map(renderVariable);
// ....
Getting Object Type Variable Drill-down
When a variable's type is Object, we often need to be able to "drill down" into its interior to access its properties. The ASTMatch.isObject method can help us determine if a variable type is an object. If it is, we can recursively render its properties.
TIP
Each layer of the variable tree is essentially a declaration (BaseVariableField). For objects, properties gives you the next-level declaration array.
use-variable-tree.tsx
import {
type BaseVariableField,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
// Only Object type variables can be drilled down
children: ASTMatch.isObject(variable.type) ? variable.type.properties.map(renderVariable) : [],
});
// ....
Getting Array Type Variable Drill-down
Similar to Object type, when encountering an Array type variable, we also want to display its internal structure. For arrays, we usually care about the type of their elements. ASTMatch.isArray can determine if a variable type is an array. It's worth noting that the element type of an array can be any type, and it might even be another array. Therefore, we need a recursive helper function getTypeChildren to handle this situation.
use-variable-tree.tsx
import {
type BaseVariableField,
type BaseType,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const getTypeChildren = (type?: BaseType): BaseVariableField[] => {
if (!type) return [];
// Get Object properties
if (ASTMatch.isObject(type)) return type.properties;
// Recursively get Array element type
if (ASTMatch.isArray(type)) return getTypeChildren(type.items);
return [];
};
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
children: getTypeChildren(variable.type).map(renderVariable),
});
// ....
scope.available
scope.available is one of the cores of the variable system, which can perform more advanced variable retrieval and monitoring actions on variables available within the scope.
When to reach for scope.available
- You need to read or validate a variable by
keyPath.
- You want to manipulate visibility outside of React hooks (e.g., in plugins or services).
- You need fine-grained subscriptions without refreshing the entire list.
useScopeAvailable
useScopeAvailable can directly return scope.available in React.
import { useScopeAvailable } from '@flowgram.ai/free-layout-editor';
const available = useScopeAvailable();
// The available object contains variable list and other APIs
console.log(available.variables);
// Get a single variable
console.log(available.getByKeyPath(['start_0', 'xxx']));
// Monitor changes in a single variable
available.trackByKeyPath(['start_0', 'xxx'], () => {
// ...
})
Main Difference from useAvailableVariables
- Return Value Different:
useAvailableVariables directly returns an array of variables, while useScopeAvailable returns a ScopeAvailableData object that includes a variables property and other methods.
- Applicable Scenario: When you need to perform more complex operations on variables, such as tracking changes in a single variable through
trackByKeyPath, useScopeAvailable is your best choice.
useScopeAvailable automatically refreshes when available variables change
If you don't want automatic refresh, you can turn it off through the autoRefresh parameter:
useScopeAvailable({ autoRefresh: false })
getByKeyPath
Through getByKeyPath, you can get a specific variable field (including variables nested in Object or Array) from the accessible variables in the current scope.
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect, useState } from 'react';
function VariableDisplay({ keyPath }: { keyPath:string[] }) {
const available = useScopeAvailable();
const variableField = available.getByKeyPath(keyPath)
return <div>{variableField.meta?.title}</div>;
}
getByKeyPath is often used in variable validation, such as:
const validateVariableInNode = (keyPath: string, node: FlowNodeEntity) => {
// Validate whether the variable can be accessed by the current node
return Boolean(node.scope.available.getByKeyPath(keyPath))
}
trackByKeyPath
When you only care about changes to a specific variable field (including variables nested in Object or Array), trackByKeyPath allows you to precisely "subscribe" to updates of that variable without causing component re-renders due to changes in other unrelated variables, thus achieving more refined performance optimization.
TIP
Combine it with autoRefresh: false to avoid wide re-renders—only update local state when the tracked variable changes.
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect, useState } from 'react';
function UserNameDisplay() {
// Turn off autoRefresh to prevent re-renders triggered by any variable changes
const available = useScopeAvailable({ autoRefresh: false });
const [userName, setUserName] = useState('');
useEffect(() => {
// Define the variable path we want to track
const keyPath = ['user', 'name'];
// Start tracking!
const disposable = available.trackByKeyPath(keyPath, (nameField) => {
// When the user.name variable field changes, this callback function will be triggered
// nameField is the changed variable field, from which we can get the latest default value
setUserName(nameField?.meta.default || '');
});
// Cancel tracking when the component unmounts to avoid memory leaks
return () => disposable.dispose();
}, [available]); // The dependency is the available object
return <div>User Name: {userName}</div>;
}
Overall Listening API
In addition to trackByKeyPath, ScopeAvailableData also provides a set of event listening APIs for overall variable changes, allowing you to more precisely control the response logic for variable changes.
This is very useful when dealing with complex scenarios that require manual management of subscriptions.
Below we use a table to compare these three core listening APIs in detail:
| API & Callback Parameters | Trigger Timing | Core Difference and Applicable Scenario |
|---|
onVariableListChange: (variables: VariableDeclaration[]) => void | When the list structure of available variables changes. | Only cares about the list itself. For example, an upstream node added/removed an output variable, causing the total number or members of available variables to change. It doesn't care about changes within variables and drill-downs. Applicable to scenarios where you need to update UI based on the presence or quantity of variable lists. |
onAnyVariableChange: (changedVariable: VariableDeclaration) => void | When the type, metadata, and drill-down fields of any variable in the list change. | Only cares about updates to variable definitions. For example, a user modified the type of an output variable. It doesn't care about changes to the list structure. Applicable to scenarios where you need to respond to changes in the content of any variable. |
onListOrAnyVarChange: (variables: VariableDeclaration[]) => void | When either of the above two situations occurs. | The most comprehensive listening, combining the previous two. Both changes to the list structure and changes to any variable will trigger it. Applicable to "fallback" scenarios where you need to respond to any possible changes. |
Let's see how to use these APIs in components through a specific example.
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect } from 'react';
function AdvancedListenerComponent() {
const available = useScopeAvailable({ autoRefresh: false });
useEffect(() => {
// 1. Listen to list structure changes
const listChangeDisposable = available.onVariableListChange((variables) => {
console.log('The structure of the available variable list has changed! The new list length is:', variables.length);
});
// 2. Listen to any variable changes
const valueChangeDisposable = available.onAnyVariableChange((changedVariable) => {
console.log(`The definition of variable '${changedVariable.keyPath.join('.')}' has changed`);
});
// 3. Listen to all changes (structure or individual variable interior)
const allChangesDisposable = available.onListOrAnyVarChange((variables) => {
console.log('The variable list or one of its variables has changed!');
// Note: The callback parameter here is the complete variable list, not a single changed variable
});
// When the component unmounts, be sure to clean up all listeners to prevent memory leaks
return () => {
listChangeDisposable.dispose();
valueChangeDisposable.dispose();
allChangesDisposable.dispose();
};
}, [available]);
return <div>Please check the console for variable change logs...</div>;
}
WARNING
These APIs all return a Disposable object. To avoid memory leaks and unnecessary calculations, you must call its dispose() method in the cleanup function of useEffect to cancel the listening.
Getting Output Variables of Current Scope
useOutputVariables
useOutputVariables can get output variables of the current scope and automatically trigger a refresh when the output variable list or drill-down changes.
const variables = useOutputVariables();
TIP
useOutputVariables is available in flowgram@0.5.6 and later versions. If you are on an earlier version, you can implement it with the following code:
const scope = useCurrentScope();
const refresh = useRefresh();
useEffect(() => {
const disposable = scope.output.onListOrAnyVarChange(() => {
refresh();
});
return () => disposable.dispose();
}, [])
const variables = scope.variables;
Other APIs
Getting Current Scope
You can get the current scope through useCurrentScope.
const scope = useCurrentScope()
scope.output.variables
scope.available
Setting Current Scope
You can set the current scope through ScopeProvider.
// set the scope of current node
<ScopeProvider scope={node.scope}>
<YourUI />
</ScopeProvider>
// set to private scope of current node
<ScopeProvider scope={node.privateScope}>
<YourUI />
</ScopeProvider>