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 variablesuseAvailableVariables
  • Need drill-down for objects/arraysASTMatch + 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 ParametersTrigger TimingCore Difference and Applicable Scenario
onVariableListChange: (variables: VariableDeclaration[]) => voidWhen 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) => voidWhen 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[]) => voidWhen 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>