# Introduction
::introduction
::
**Rete.js** (pronounced /หriห.ti/, meaning "network" in Italian) is a framework for creating visual interfaces and workflows. It provides out-of-the-box solutions for visualization using various libraries and frameworks, as well as solutions for processing graphs based on dataflow and control flow approaches.
::alert
**You are reading the documentation for Rete.js v2**
- Rete.js v1 documentation is still available at [rete.js.org](https://rete.js.org){rel="nofollow"}
- Upgrading from Rete.js v1? Check out the [Migration](https://retejs.org/docs/migration) guide
::
In the interactive example above, you can see the notation of the basic elements of the classic editor view and the graph processing using the Dataflow-based engine. This demonstrates the essential capabilities of the framework:
- **Visualization**: you can choose React.js, Vue.js, Angular, Svelte or Lit to visualize nodes, sockets, controls, and connections. These visual components can be tailored to your specific needs by creating custom components for each framework, and they can all coexist in a single editor.
- **Processing**: the framework offers various types of engines that enable processing diagrams based on their nature, including dataflow and control flow. These types can be combined within the same graph.
::youtube-video{name="intro"}
::
## Ecosystem
The framework is made up of various packages, including the core `rete` package and different plugins.
This approach provides flexibility in choosing only the required packages for your use case, avoiding unnecessary dependencies. It also offers the flexibility to choose different versions (e.g., testing out an RC version of a specific plugin) and substitute these packages with your custom builds (forks or plugins created from scratch).
Each of packages has its own repository, providing development flexibility but also adding overhead. For this reason, **Rete CLI** was created to build TypeScript modules with built-in ESLint and Jest support, reducing boilerplate code and providing basic tools for plugin development and debugging, ultimately saving developers time.
Rete.js provides various handy resources and tools to get you started quickly:
- **Examples**: check out the [Examples](https://retejs.org/examples) page or [Codesandbox](https://codesandbox.io/search?refinementList%5Btags%5D%5B0%5D=rete.js){rel="nofollow"} for code samples
- **Rete Kit**: easily generate a basic project for your framework of choice using this tool. For more details, visit the [Rete Kit](https://retejs.org/docs/development/rete-kit) page.
- **Guides**: get hands-on experience by following the [step-by-step guides](https://retejs.org/docs/guides/basic). This will enable you to explore the existing features and learn how to extend and customize the framework to your needs
# Getting started
::references
:::ref-example{link="/examples" title="Examples"}
:::
:::ref-guide{link="/docs/development/rete-kit" title="Rete Kit"}
:::
:::ref-external
---
link: https://codesandbox.io/s/rete-js-v2-yrzxe5
title: Codesandbox
---
:::
:::ref-external{link="https://codepen.io/Ni55aN/pen/rNZKejd" title="Codepen"}
:::
::
There are two easy ways to begin working with the framework: forking [the examples](https://retejs.org/examples) on Codesandbox or creating a local application using [Rete Kit](https://retejs.org/docs/development/rete-kit). The first option allows for experimentation with functionality, which not always covered in the guides. Alternatively, the second option enables the creation of a local application with specific node editor features for a chosen version of **React.js**, **Vue.js**, **Angular**, **Svelte** or **Lit**. Afterward, following the guides will help familiarize yourself with the framework's features and capabilities.
::youtube-video{name="getting-started"}
::
## Prerequisites
Before diving into Rete.js, it's important to have an understanding of JavaScript or TypeScript fundamentals. The framework is primarily designed with TypeScript in mind, with examples and guides showcasing code in this language. However, for newcomers to TypeScript or those looking to quickly prototype, it's still possible to use Rete.js directly in JavaScript code.
If TypeScript is your preferred choice, make sure you have TypeScript version 4.7 or higher installed.
## Playgrounds
- [Codesandbox](https://codesandbox.io/s/rete-js-v2-yrzxe5){rel="nofollow"}
- [Codepen](https://codepen.io/Ni55aN/pen/rNZKejd){rel="nofollow"}
## Creating an application using devkit
Use [Rete Kit](https://retejs.org/docs/development/rete-kit) to quickly set up a Rete.js application. It lets you select a stack (**React.js**, **Vue.js**, **Angular**, **Svelte** or **Lit**) and the set of features.
## Adding Rete.js to your application
Framework packages are available on NPM and support common formats like ES (.esm.js), CommonJS (.common.js), and UMD (.min.js).
The command below provides an example of how to install the framework packages for the latest version.
```bash
npm i rete rete-area-plugin rete-connection-plugin rete-render-utils rete-react-plugin react react-dom
```
For specific information on the required packages, refer [one of the guides](https://retejs.org/docs/guides/basic)
## Usage from CDN
Framework packages are also available on numerous CDNs that serve npm packages. To add them to an HTML page, use the following example:
```html
```
Use these packages by accessing their namespace, which can be found in the `name` field of `rete.config.ts` file for each package. Make sure to add the required peer dependencies
```js
Rete
ReteAreaPlugin
ReteConnectionPlugin
ReteReactPlugin
```
Furthermore, you can integrate them on platforms like [Codepen](https://codepen.io){rel="nofollow"} using [esm.sh](https://esm.sh){rel="nofollow"}.
# LLMs.txt
Enhance your visual programming experience with AI tools that understand Rete.js node editor patterns, dataflow concepts, and plugin architecture.
## What is LLMs.txt?
Building node editors and visual programming interfaces involves unique patterns and concepts that differ from traditional application development. Our AI integration through [LLMs.txt](https://llmstxt.org/){rel="nofollow"} helps coding assistants understand:
- **Node-based architecture** and connection patterns
- **Dataflow vs Control flow** processing paradigms
- **Plugin composition** for extensible editors
- **Multi-framework rendering** (React, Vue, Angular, Svelte)
- **Graph processing** and manipulation techniques
This enables AI tools to provide contextually accurate suggestions for your visual programming projects.
## Resources
Our documentation is optimized for AI consumption through specialized endpoints that understand visual programming workflows:
- **[llms.txt](https://retejs.org/llms.txt){rel="nofollow"}** - Essential node editor concepts, core APIs, and quick reference patterns
- **[llms-full.txt](https://retejs.org/llms-full.txt){rel="nofollow"}** - Complete visual programming knowledge base including advanced patterns, plugin architecture, and processing engines
These endpoints are continuously updated to reflect the latest in node editor development practices and framework capabilities.
## How to Use
You can integrate Rete.js documentation with any AI coding assistant by providing the LLMs.txt URLs as context. This gives the AI comprehensive knowledge about node editor development patterns and best practices.
### Basic integration steps
1. Copy the LLMs.txt URL: `https://retejs.org/llms.txt` (or `llms-full.txt` for complete docs)
2. Reference it in your AI tool's context or conversation
3. Ask questions about Rete.js development, node editor patterns, or get code suggestions
### VS Code GitHub Copilot
Add the LLMs.txt URL to your workspace for enhanced context-aware suggestions:
1. Create a `.copilot-instructions.md` file in your project root
2. Add the reference:
```markdown
# Copilot Instructions
For Rete.js development, reference the documentation at:
https://retejs.org/llms-full.txt
Focus on node editor patterns, dataflow programming, and plugin architecture.
```
3. Copilot will now understand Rete.js concepts when providing code completions and suggestions
### Google Gemini Chat
Reference the documentation directly in your conversation:
```text
I'm building a node editor with Rete.js. Please review the documentation at https://retejs.org/llms-full.txt and help me create a custom node that processes image data through multiple transformation steps.
```
### Model Context Protocol (MCP)
MCP-compatible tools like Context7 automatically discover and fetch the Rete.js documentation as context to AI models:
1. The complete Rete.js documentation is automatically loaded as context for AI conversations
2. AI models have persistent access to node editor patterns, plugin architecture, and best practices
3. No manual setup required - the context is always available
This approach ensures every AI interaction has comprehensive Rete.js knowledge without any configuration.
# Plugin system
::references
:::ref-github
---
link: https://github.com/retejs/rete/blob/main/src/scope.ts
title: Source code
---
:::
::
Plugins offer the ability to add new functionality mostly through a single entry point. They communicate with each other using signals that propagate from parent plugins to child plugins. Since plugins can have multiple child plugins, these signals are passed through in the order they are connected (it can be important when including plugins such as `rete-readonly-plugin`)
::diagram{caption="Architecture" name="plugin-system/architecture"}
::
The following code example demonstrates the creation of two scopes: the parent and the child. Both scopes log signals.
```ts
import { Scope } from 'rete';
const parentScope = new Scope('parent'); // number is produced type
const childScope = new Scope('child'); // [number] is expected types of parent chain
parentScope.addPipe((context) => { // add pipe to parent scope
console.log('parent', context); // number
return context;
});
childScope.addPipe((context) => { // add pipe to child scope
console.log('child', context); // string | number
return context;
});
parentScope.use(childScope); // forward all signals to child scope
const returnedNumber = await parentScope.emit(1); // can emit number
const returnedString = await childScope.emit('a'); // can emit string
```
Keep in mind that the order of `use` and `addPipe` affects the order in which the parent and child handlers are called.
Logs:
```log
parent 1
child 1
child a
```
Signals can be modified or prevented in some cases.
::diagram{caption="Prevent and modify" name="plugin-system/addPipe"}
::
```ts
parentScope.addPipe((context) => {
return context * 2;
});
childScope.addPipe((context) => {
if (context === 'b') return // prevent propagation of 'b'
return context;
});
const doubledNumber = await parentScope.emit(1); // 2
const expectedString = await childScope.emit('a'); // 'a'
const expectedUndefined = await childScope.emit('b'); // undefined
```
Static typing is used to ensure that the expected signals of used plugins are compatible with those produced by its parent plugin.
```ts
import { Scope } from 'rete';
const parentScope = new Scope('parent');
const childScope = new Scope('child');
parentScope.use(childScope); // Type 'boolean' is not assignable to type 'string | number'.ts(2345)
```
Child plugins can access the instance of the parent plugin both for direct access to its interfaces and for producing signals on behalf of the parent plugin
```ts
import { Scope } from 'rete';
class Root extends Scope {
isRoot = true
}
class Root2 extends Scope {
isRoot2 = true
}
const parentScope = new Root('parent');
const childScope = new Scope('child');
parentScope.use(childScope);
const parent = childScope.parentScope(); // Root instance, but Scope from TS perspective
const root = childScope.parentScope(Root); // Root instance
const wrongInstance = childScope.parentScope(Root2); // throws exception
```
# Presets
A preset is a set of pre-built functionality that typically forms the foundation of an editor but can be replaced with another preset from the same category or a custom one.
::diagram{caption="Architecture" name="presets/architecture"}
::
## Data structures
For example, there is a classic editor preset that provides classes such as Node, Connection, Input, Output, and Socket.
```ts
import { ClassicPreset } from 'rete';
const { Node, Connection, Socket, Input, Output, Control } = ClassicPreset
```
## Rendering
In addition, each render plugin has preset for displaying classic nodes based on the data structures mentioned above.
```ts
import { ReactPlugin, Presets as ReactPresets } from 'rete-react-plugin'
const reactPlugin = new ReactPlugin({ createRoot })
reactPlugin.addPreset(ReactPresets.classic.setup())
reactPlugin.addPreset(ReactPresets.contextMenu.setup())
```
## Interaction
`rete-connection-plugin` comes with presets, one of which replicates the connection interaction functionality from framework v1. Additionally, an alternative preset with a simpler way to interact with connections has been included.
```ts
import { ConnectionPlugin, Presets as ConnectionPresets } from 'rete-connection-plugin'
const connection = new ConnectionPlugin()
connection.addPreset(ConnectionPresets.classic.setup())
```
## Conclusion
In essence, the presets can be used in any scenario that involves the need to implement a particular functionality through the use of one or more alternative approaches.
# Editor
::diagram{caption="NodeEditor" name="editor/node-editor"}
::
`NodeEditor` is a class that implements an interface for interacting with a graph. Similar to other modules, it extends `Scope`: can produce signals and provides the ability to connect plugins.
```ts
import { NodeEditor, BaseSchemes } from 'rete'
type Schemes = BaseSchemes // has Node { id: string } and Connection { id: string, source: string, target: string }
const editor = new NodeEditor()
```
The `Schemes` type will be used for further type inference purposes.
There is a [classic preset](https://retejs.org/docs/concepts/presets#data-structures) that provides the interfaces for building nodes.
The editor is applicable on both the client and server sides. On the client side, it can provide data for visualization purposes. On the server side, it can provide data for graph processing, for example, through `rete-engine` or other interactions using `rete-structures`.
## Node and Connection management
::diagram{caption="ClassicPreset" name="editor/classic-preset"}
::
We can add nodes as a regular object with a mandatory `id` field, or as nodes from `ClassicPreset`
```ts
import { ClassicPreset } from 'rete'
const node = new ClassicPreset.Node('Label')
node.addOutput('output', new ClassicPreset.Output(socket, 'Title'))
await editor.addNode(node)
```
Removing can be achieved with `id`
```ts
await editor.removeNode(node.id)
```
To create a connection, you can use a basic object with mandatory fields (`id`, `source`, `target`) or a classic preset that requires the source and target nodes to be passed for TypeScript type checking.
```ts
import { ClassicPreset } from 'rete'
const connection = new ClassicPreset.Connection(sourceNode, 'portKey', targetNode, 'portKey')
await editor.addConnection(connection)
```
Removing the connection by `id`
```ts
await editor.removeConnection(connection.id)
```
## Create a 2D area
::diagram{caption="AreaPlugin" name="editor/area"}
::
In order to visualize on HTML, `rete-area-plugin` is necessary. This plugin is responsible for basic features, such as zooming and dragging, and serves as an entry point for other plugins for visualizing and interacting with users
```ts
import { AreaPlugin } from 'rete-area-plugin'
const area = new AreaPlugin(container) // container is HTMLElement where the area will be inserted
```
The `AreaExtra` type is necessary for incorporating other signal types, such as rendering various types of elements aside from `node` and `connection`
This plugin includes extensions. Some of them implement the functionality of v1, but with one significant difference - they are optional. For instance, the node selection extension not only supports node selection, but it is expandable (check out [Comments](https://retejs.org/examples/comments) example), but it can also be substituted with an alternative implementation.
```ts
import { AreaExtensions } from 'rete-area-plugin'
AreaExtensions.selectableNodes(area, AreaExtensions.selector(), {
accumulating: AreaExtensions.accumulateOnCtrl()
})
```
## Interaction with connections
::diagram{caption="ConnectionPlugin" name="editor/connection"}
::
The `rete-connection-plugin` plugin is responsible for user interaction with connections (creation, deletion)
```ts
import { BidirectFlow, ConnectionPlugin, Presets as ConnectionPresets } from 'rete-connection-plugin'
const connection = new ConnectionPlugin()
connection.addPreset(ConnectionPresets.classic.setup())
```
Unlike Rete.js v1, this plugin doesn't render connections.
## Rendering
::diagram{caption="Rendering" name="editor/rendering"}
::
The rendering of the UI is exclusively handled by rendering plugins (with a few exceptions), which provide presets for various kinds of functionality.
Let's take a look at the example using `rete-react-plugin`
```ts
import { ReactArea2D, ReactPlugin, Presets as ReactPresets } from 'rete-react-plugin'
import { MinimapExtra } from 'rete-minimap-plugin'
import { ContextMenuExtra } from 'rete-context-menu-plugin'
type AreaExtra =
| ReactArea2D
| ContextMenuExtra
| MinimapExtra
const reactPlugin = new ReactPlugin()
reactPlugin.addPreset(ReactPresets.classic.setup())
reactPlugin.addPreset(ReactPresets.contextMenu.setup())
reactPlugin.addPreset(ReactPresets.minimap.setup())
area.use(reactPlugin)
```
Every element in the editor, like node, control, socket, or connection, is technically an independent tree of elements, which offers flexibility in combining different rendering frameworks. More information is available in the [Integration](https://retejs.org/docs/concepts/integration) article.
# Engine
::diagram{caption="Architecture" name="engine/architecture"}
::
The `rete-engine` is a package that implements two approaches for processing graph: [Dataflow](https://retejs.org/#dataflow) and [Control flow](https://retejs.org/#control-flow)
### Dataflow
The dataflow approach is focused solely on data, where the target node requests data from incoming nodes. Graph processing proceeds from left to right, passing the output of nodes as input arguments to outgoing nodes.
::diagram{caption="Dataflow" name="engine/dataflow"}
::
This approach is commonly used in products with node editors such as Blender.
The code below represents the basic constructs required for the `DataflowEngine` to work:
- **Nodes must implement a `data` method**: this method accepts data from incoming nodes
- **Connect the engine to the editor**: the engine will register every added node for further processing
- **Fetching node data**: `fetch` initiates a graph traversal starting from the target node and visiting all its predecessors
```ts
import { ClassicPreset } from 'rete-engine'
import { DataflowEngine } from 'rete-engine'
const { Node, Socket } = ClassicPreset
class NodeAdd extends Node<{ left: Socket, right: Socket }, { value: Socket }, { }> {
constructor() {
// init controls and ports
}
// mandatory method
data(inputs: { left?: number[], right?: number[] }): { value: number } {
const left = inputs.left[0] || 0
const right = inputs.right[0] || 0
return {
value: left + right
}
}
}
const engine = new DataflowEngine()
editor.use(engine)
const nodeOutput = await engine.fetch(resultNode.id)
```
### Control flow
Control flow is a node traversal approach that allows you to determine how to pass control to the next nodes.
::diagram{caption="Control flow" name="engine/control-flow"}
::
The processing starts at the start node, which specifies how control is passed through its outgoing connections. For instance, it can be a delay or a branching. The closest example is UE4 Blueprints.
```ts
import { ControlFlowEngine } from 'rete-engine'
const { Node, Socket } = ClassicPreset
class Log extends Node<{ enter: Socket }, { out: Socket }, {}> {
constructor() {
// init ports
}
// mandatory method
execute(input: 'enter', forward: (output: 'out') => void) {
console.log('log something')
forward('out')
}
}
const engine = new ControlFlowEngine()
editor.use(engine)
engine.execute(startNode.id)
```
## Hybrid
In addition, these approaches can be combined. For example, ports named 'exec' can be used to control flow, while other ports manage data.
```ts
const controlflow = new ControlFlowEngine(node => {
return {
inputs: () => ['exec'],
outputs: () => ['exec']
}
})
const dataflow = new DataflowEngine(({ inputs, outputs }) => {
return {
inputs: () => Object.keys(inputs).filter(name => name !== 'exec'),
outputs: () => Object.keys(outputs).filter(name => name !== 'exec')
}
})
```
Alternatively, you can use the `Dataflow` and `ControlFlow` classes directly, affording more precise control over the graph processing.
```ts
import { ControlFlow, Dataflow } from 'rete-engine'
const control = new ControlFlow(editor)
const dataflow = new Dataflow(editor)
control.add(startNode, {
inputs: () => [],
outputs: () => ['exec'],
async execute(input, forward) {
const inputs = await dataflow.fetchInputs(startNode.id)
forward('exec')
}
})
dataflow.add(startNode, {
inputs: () => ['data'],
outputs: () => ['data'],
data(fetchInputs) {
const inputs = await fetchInputs()
const data = {
data: inputs.data[0] // forward input data (assuming there is only one input connection to port "data")
}
return data
}
})
```
## Conclusion
This engine version incorporates revised approaches to graph processing and addresses the shortcomings of the previous version, which was initially geared towards strict dataflow without recursion support.
When it comes to graph processing, there's no one-size-fits-all solution. In simple cases, you can use `DataflowEngine` and `ControlFlowEngine`, while in more complex cases, you can use `ControlFlow` and `Dataflow` or write your own solution by studying [the source code](https://github.com/retejs/engine){rel="nofollow"} of the `rete-engine` package
# Integration
::diagram{caption="Architecture" name="integration/architecture"}
::
This framework is not bound to any UI rendering framework and can be integrated with the most popular frameworks/libraries such as **Angular**, **Svelte**, **Lit**, **Vue.js**, **React.js**. The following rendering packages are available:
- [`rete-react-plugin`](https://www.npmjs.com/package/rete-react-plugin){rel="nofollow"}
- [`rete-vue-plugin`](https://www.npmjs.com/package/rete-vue-plugin){rel="nofollow"}
- [`rete-angular-plugin`](https://www.npmjs.com/package/rete-angular-plugin){rel="nofollow"}
- [`rete-svelte-plugin`](https://www.npmjs.com/package/rete-svelte-plugin){rel="nofollow"}
- [`@retejs/lit-plugin`](https://www.npmjs.com/package/@retejs/lit-plugin){rel="nofollow"}
The primary objective is to empower you to choose the visualization tools that align with your specific needs. Additionally, if you ever need to use the render plugin for a different framework within your application (such as during a project migration), you can easily do so. Please note that `rete-angular-plugin` is only compatible with Angular applications.
## Classic preset
::diagram{caption="Presets" name="integration/presets"}
::
By default, you can use the classic preset that has built-in components for:
- nodes
- connections
- some controls (numeric or text input fields)
```ts
import { AngularPlugin, AngularArea2D, Presets as AngularPresets } from 'rete-angular-plugin'
const angular = new AngularPlugin>({ injector })
angular.addPreset(AngularPresets.classic.setup())
area.use(angular)
```
The framework allows you to swap out the pre-defined components with any other components as per your needs. The node component, in particular, can be customized extensively. Refer to the [Customization](https://retejs.org/docs/guides/renderers/react#customization) guide for more details
## Combining render plugins
::diagram{caption="Combine" name="integration/combine"}
::
This framework version has improved approaches for combining various rendering frameworkswhile ensuring TypeScript support. For instance, you can render one node using **Vue.js** and another using **React.js**.
```ts
import { ReactArea2D, ReactPlugin, Presets as ReactPresets } from 'rete-react-plugin'
import { VueArea2D, VuePlugin, Presets as VuePresets } from 'rete-vue-plugin'
type AreaExtra =
| ReactArea2D
| VueArea2D
const reactPlugin = new ReactPlugin()
const vuePlugin = new VuePlugin()
reactPlugin.addPreset(ReactPresets.classic.setup({ customize: {
node(data) {
if (data.payload instanceof AddNode) return null // prevent rendering of AddNode by React.js
return ReactPresets.classic.Node
}
} }))
vuePlugin.addPreset(VuePresets.classic.setup({ customize: {
node() {
return VuePresets.classic.Node // render all nodes that weren't rendered by previously used render plugin
}
} }))
// order matters
area.use(reactPlugin)
area.use(vuePlugin)
```
The `AddNode` node in this example is rendered using **Vue.js**, while all other nodes are rendered using **React.js**.
Using multiple frameworks at once may have performance drawbacks, but it can also offer a significant boost to prototyping speed when when time-to-market is critical.
# Basic editor
::alert
This guide features the `rete-react-plugin`. You can use it in any application, regardless of the framework you're using (**React.js**, **Vue.js**, **Angular**, **Svelte** etc.).
Follow this guide to use the corresponding render plugin in your **Angular**, **Svelte** or **Vue.js** application, with reference to the respective guides for [Angular](https://retejs.org/docs/guides/renderers/angular), [Svelte](https://retejs.org/docs/guides/renderers/svelte), [Vue.js](https://retejs.org/docs/guides/renderers/vue) etc.
::
::references
:::ref-example{link="/examples/basic" title="Basic"}
:::
:::ref-guide{link="/docs/guides/data-structures" title="Data structures"}
:::
:::ref-github{link="https://github.com/retejs/rete" title="Core"}
:::
:::ref-github{link="https://github.com/retejs/area-plugin" title="Area plugin"}
:::
:::ref-github
---
link: https://github.com/retejs/connection-plugin
title: Connection plugin
---
:::
::
In order to complete this guide, you can create an application on **[Codesandbox](https://codesandbox.io){rel="nofollow"}** by choosing any client application template or set up the application locally.
## Install dependencies
::kit
::
```bash
npm i rete rete-area-plugin rete-connection-plugin rete-react-plugin rete-render-utils styled-components react@18 react-dom@18
```
## Defining types and initializing the editor instance
```ts
import { NodeEditor, GetSchemes, ClassicPreset } from "rete";
type Schemes = GetSchemes<
ClassicPreset.Node,
ClassicPreset.Connection
>;
const editor = new NodeEditor();
```
where `Schemes` is a type that will assist you with type inference during plugin configuration. For more intricate examples, it may be necessary to extend the `ClassicPreset.Node` and `ClassicPreset.Connection` classes.
## Add an arbitrary node
Creating a node that contains one control and the output port. Keep in mind that `addNode` is an asynchronous method.
```ts
const socket = new ClassicPreset.Socket("socket");
const nodeA = new ClassicPreset.Node("A");
nodeA.addControl("a", new ClassicPreset.InputControl("text", {}));
nodeA.addOutput("a", new ClassicPreset.Output(socket));
await editor.addNode(nodeA);
```
## Create an area to render with React.js
Place this code before calling `addNode`:
```ts
import { createRoot } from "react-dom/client";
import { AreaPlugin } from "rete-area-plugin";
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
type AreaExtra = ReactArea2D;
const area = new AreaPlugin(container);
const render = new ReactPlugin({ createRoot });
render.addPreset(Presets.classic.setup());
editor.use(area);
area.use(render);
```
where
- `container` is the HTMLElement where the editor will be placed
- `AreaExtra` type enables the inclusion of custom viewable elements, as only `node` and `connection` are available by default
## Adding another node
```ts
const nodeB = new ClassicPreset.Node("B");
nodeB.addControl("b", new ClassicPreset.InputControl("text", {}));
nodeB.addInput("b", new ClassicPreset.Input(socket));
await editor.addNode(nodeB);
```
Let's establish connection between these nodes
```ts
await editor.addConnection(new ClassicPreset.Connection(nodeA, "a", nodeB, "b"));
```
## Node positioning
The screen will display two overlapping nodes, but we can adjust the position of the second node.
```ts
await area.translate(nodeB.id, { x: 270, y: 0 });
```
## Interactive connections
This feature enables users to interact with the nodes.
```ts
import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin"
const connection = new ConnectionPlugin();
connection.addPreset(ConnectionPresets.classic.setup())
area.use(connection);
```
## Fit viewport
Use the `zoomAt` extension to automatically adjust the viewport to fit all nodes.
```ts
import { AreaExtensions } from "rete-area-plugin";
AreaExtensions.zoomAt(area, editor.getNodes());
```
By default, node dimensions are calculated using `clientWidth`/`clientHeight`. If this method is called right after appending nodes, it might not work correctly until the element is visible on the screen. Instead, you can specify `width`/`height` properties in the node class, as [demonstrated in this step](https://retejs.org/docs/guides/arrange#create-node-base).
## Selectable nodes
Additionally, extensions offer various capabilities, like enabling the user to select nodes.
```ts
AreaExtensions.selectableNodes(area, AreaExtensions.selector(), {
accumulating: AreaExtensions.accumulateOnCtrl()
});
```
For further details, check out the [Selectable](https://retejs.org/docs/guides/selectable) guide.
## Nodes order
If your application allows node selection, users may want to view selected nodes without any visual obstructions. To facilitate this, an additional extension is provided that automatically brings the selected node to the front.
```ts
AreaExtensions.simpleNodesOrder(area);
```
Check out the complete result on the [Basic](https://retejs.org/examples/basic/react) example page.
# React.js
::alert
This guide is an extension of the [Basic](https://retejs.org/docs/guides/basic) guide and provides in-depth instructions for using the `rete-react-plugin`
::
::references
:::ref-example{link="/examples/basic/react" title="Basic"}
:::
:::ref-example{link="/examples/controls/react" title="Controls"}
:::
:::ref-example{link="/examples/customization/react" title="Customization"}
:::
:::ref-github{link="https://github.com/retejs/react-plugin" title="Plugin"}
:::
:::ref-external{link="https://react.dev/" title="React.js"}
:::
:::ref-guide{link="/docs/guides/context-menu" title="Context menu"}
:::
:::ref-guide{link="/docs/guides/minimap" title="Minimap"}
:::
:::ref-guide{link="/docs/guides/reroute" title="Reroute"}
:::
::
This plugin uses a classic preset that includes visual components for nodes, connections, sockets, and input controls. It leverages `styled-components` to design these components.
This plugin can be used in any application, regardless of your stack (**React.js**, **Vue.js**, **Angular**, etc.).
## Install dependencies
::kit
::
```bash
npm i rete-react-plugin rete-render-utils styled-components
```
If you're using this plugin in an application that doesn't utilize **React.js**, make sure to install the required **React.js** dependencies as well.
```bash
npm i react@18 react-dom@18
```
## Plugin connection
```ts
import { createRoot } from "react-dom/client";
import { AreaPlugin } from "rete-area-plugin";
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
type AreaExtra = ReactArea2D;
// ....
const render = new ReactPlugin({ createRoot });
render.addPreset(Presets.classic.setup());
area.use(render);
```
Check out the [Basic](https://retejs.org/examples/basic/react) page for an example of how to use this rendering plugin.
## Using React.js 16
In case you're using React.js version 16 (or 17), just remove the `createRoot` method
```ts
const render = new ReactPlugin();
```
## "useRete" hook
When working with React app, `useRete` hook eliminates the need for boilerplate code that binds an editor to HTML element. This becomes particularly crucial for dynamic app updates where the old instance of the editor must be removed and replaced with a new one.
```tsx
import { useRete } from 'rete-react-plugin';
function App() {
const [ref, editor] = useRete(createEditor)
return
}
```
where `createEditor` should return object with `destroy` method (usually it has `area.destroy()` call)
## Controls
This plugin provides built-in controls that are displayed based on the following objects:
- `ClassicPreset.InputControl` as `` or ``
Simply add the control to the node
```ts
node.addControl('my-control', new ClassicPreset.InputControl("number", {
initial: 0,
readonly: false,
change(value) { }
}))
```
If you want to add different types of controls, you can return the necessary functional component in the `control` handler of `customize` property.
```tsx
render.addPreset(Presets.classic.setup({
customize: {
control(context) {
if (context.payload.isButton) {
return (props: { data: { isButton: true, label: string, onClick: () => void }}) => (
)
}
if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in Presets.classic.Control
return Presets.classic.Control;
}
}
}
}));
node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
```
This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in [the example](https://retejs.org/examples/controls/react)
Make sure to call `stopPropagation` in `onPointerDown` to prevent the area from intercepting events such as `click`. If you are encountering this issue in React 16 or your interactive elements are added to a custom node instead of a control, try the following solution:
```tsx
import { Drag } from "rete-react-plugin";
```
Or use a hook to avoid extra nesting
```tsx
const ref = React.useRef(null)
Drag.useNoDrag(ref)
```
## Customization
In a similar manner to the approach outlined above, you can replace node, connection, or socket components.
### Node styles
The easiest approach is to extend the current component and use `styled-components` to add extra styles.
```tsx
import { Presets } from "rete-react-plugin";
import { css } from "styled-components";
const myStyles = css<{ selected?: boolean }>`
background: white;
${(props) => props.selected && css`
border-color: red;
`}
`;
function StyledNode(props: { data: Schemes['Node'] }) {
return myStyles} {...props} />;
}
render.addPreset(Presets.classic.setup({
customize: {
node() {
return StyledNode
}
}
}))
```
Implementing this will result in all your nodes using `myStyles`.
### Specific nodes
You can add an extra condition to apply these styles only to specific nodes.
```ts
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "White") {
return StyledNode;
}
return Presets.classic.Node;
}
}
}))
await editor.addNode(new ClassicPreset.Node('White'))
```
### Full node customization
If you want to completely change the node structure, you can implement your own component similar to [Node.tsx](https://github.com/retejs/react-plugin/blob/next/src/presets/classic/components/Node.tsx){rel="nofollow"} from the classic preset.
```ts
import { CustomNode } from './CustomNode'
render.addPreset(Presets.classic.setup({
customize: {
node() {
return CustomNode
}
}
}))
```
The implementation of `CustomNode` is available in the **CustomNode.tsx** file of the [Customization for React.js](https://retejs.org/examples/customization/react) example.
### Full customization of connections
Use **Connection.tsx** as a starting point from the [presets/classic/components](https://github.com/retejs/react-plugin/blob/next/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import { CustomConnection } from './CustomConnection'
render.addPreset(Presets.classic.setup({
customize: {
connection() {
return CustomConnection
}
}
}))
```
### Full socket customization
Use **Socket.tsx** as a starting point from the [presets/classic/components](https://github.com/retejs/react-plugin/blob/next/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import { CustomSocket } from './CustomSocket'
render.addPreset(Presets.classic.setup({
customize: {
socket() {
return CustomSocket
}
}
}))
```
## Customize context menu
As the context menu components utilize `styled-components`, you can customize their styles by:
```ts
import styled from "styled-components";
const { Menu, Common, Search, Item, Subitems } = Presets.contextMenu
const CustomMenu = styled(Menu)`
width: 320px;
`
const CustomItem = styled(Item)`
background: grey;
`
render.addPreset(Presets.contextMenu.setup({
customize: {
main: () => CustomMenu,
item: () => CustomItem,
common: () => Common,
search: () => Search,
subitems: () => Subitems
}
}))
```
## Other presets
- [context menu](https://retejs.org/docs/guides/context-menu)
- [minimap](https://retejs.org/docs/guides/minimap)
- [reroute](https://retejs.org/docs/guides/reroute)
Check out the complete result on the [Customization for React.js](https://retejs.org/examples/customization/react) example page.
# Vue.js
::alert
This guide is an extension of the [Basic](https://retejs.org/docs/guides/basic) guide and provides instructions for using the `rete-vue-plugin` instead of `rete-react-plugin`
::
::references
:::ref-example{link="/examples/basic/vue" title="Basic"}
:::
:::ref-example{link="/examples/customization/vue" title="Customization"}
:::
:::ref-example{link="/examples/controls/vue" title="Controls"}
:::
:::ref-github{link="https://github.com/retejs/vue-plugin" title="Plugin"}
:::
:::ref-external{link="https://vuejs.org/" title="Vue.js"}
:::
:::ref-guide{link="/docs/guides/context-menu" title="Context menu"}
:::
:::ref-guide{link="/docs/guides/minimap" title="Minimap"}
:::
:::ref-guide{link="/docs/guides/reroute" title="Reroute"}
:::
::
This plugin offers a classic preset that comes with visual components for nodes, connections, sockets, and input controls.
Supports both versions of Vue.js: 2 and 3
You can use this plugin in any application, irrespective of the application stack (React.js, Vue.js, Angular). However, using SFC requires a bundler with a corresponding template compiler installed, which is a separate topic for discussion.
## Install dependencies
::kit
::
```bash
npm i rete-vue-plugin rete-render-utils
```
## Plugin connection
```ts
import { AreaPlugin } from "rete-area-plugin";
import { VuePlugin, Presets, VueArea2D } from "rete-vue-plugin";
type AreaExtra = VueArea2D;
// ....
const render = new VuePlugin();
render.addPreset(Presets.classic.setup());
area.use(render);
```
Check out the [Vue] page(/examples/basic/vue) page for an example usage of this render plugin.
## Using Vue.js 2
To use the plugin with Vue 2, add `/vue2` to the import statement.
```ts
import { VuePlugin, Presets, VueArea2D } from "rete-vue-plugin/vue2";
```
## Controls
This plugin provides built-in controls that are displayed based on the following objects:
- `ClassicPreset.InputControl` as `` or ``
Simply add the control to the node
```ts
node.addControl('my-control', new ClassicPreset.InputControl("number", {
initial: 0,
readonly: false,
change(value) { }
}))
```
If you want to add different types of controls, you can return the necessary component in the `control` handler of `customize` property.
```tsx
import MyButton from './MyButton.vue'
render.addPreset(Presets.classic.setup({
customize: {
control(context) {
if (context.payload.isButton) {
return MyButton
}
if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in Presets.classic.Control
return Presets.classic.Control;
}
}
}
}));
node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
```
**MyButton.vue**
```vue
```
This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in [the example](https://retejs.org/examples/controls/vue)
Make sure to specify `@pointerdown.stop` to prevent the area from intercepting events such as `click`.
## Customization
In a similar manner to the approach outlined above, you can replace node, connection, or socket components.
### Customization of all nodes
If you want to completely change the node structure, you can implement your own component similar to [Node.vue](https://github.com/retejs/vue-plugin/blob/next/src/presets/classic/components/Node.vue){rel="nofollow"} from the classic preset
```ts
import CustomNode from './CustomNode.vue'
render.addPreset(Presets.classic.setup({
customize: {
node() {
return CustomNode
}
}
}))
```
The implementation of `CustomNode` is available in the **CustomNode.vue** file of the [Customization for Vue.js](https://retejs.org/examples/customization/vue) example.
### Specific nodes
You can add an extra condition to apply this component only to specific nodes.
```ts
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "Custom") {
return CustomNode;
}
return Presets.classic.Node;
}
}
}))
await editor.addNode(new ClassicPreset.Node('White'))
```
### Connection customization
Use **Connection.vue** as a starting point from the [presets/classic/components](https://github.com/retejs/vue-plugin/blob/next/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import CustomConnection from './CustomConnection.vue'
render.addPreset(Presets.classic.setup({
customize: {
connection() {
return CustomConnection
}
}
}))
```
### Socket customization
Use **Socket.vue** as a starting point from the [presets/classic/components](https://github.com/retejs/vue-plugin/blob/next/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import CustomSocket from './CustomSocket.vue'
render.addPreset(Presets.classic.setup({
customize: {
socket() {
return CustomSocket
}
}
}))
```
## Customize context menu
In order to customize the context menu for this rendering plugin, one can override styles using selectors (and it's important to consider the specificity of selectors in CSS)
```scss
[rete-context-menu] {
width: 320px !important;
.block:first-child input {
background: grey;
}
.block.item {
background: grey;
}
}
```
## Use Vue.js plugins
Since `rete-vue-plugin` creates independent Vue.js instance for nodes, sockets, controls, etc., it doesn't inherit plugins from your project's main Vue instance. To bridge this gap, the plugin offers a solution: injecting a custom Vue application instance. This capability ensures that any Vue plugins or global components you wish to employ within your Rete-specific Vue components are accessible, enabling seamless sharing between your Vue.js application and Rete.js editor.
### Vue.js 3
The following example demonstrates how to configure custom Vue.js 3 instance:
```ts
import { Presets, VuePlugin } from "rete-vue-plugin";
import { createApp } from "vue";
const render = new VuePlugin({
setup(context) {
const app = createApp(context);
app.use(yourPlugin);
return app;
},
});
```
where `yourPlugin` is an instance of any plugin (like [Vuetify](https://vuetifyjs.com/en/getting-started){rel="nofollow"} or [Vue I18N](https://vue-i18n.intlify.dev/){rel="nofollow"})
### Vue.js 2
Since the initialization for Vue.js 2 is slightly different, let's take a look at the following example:
```ts
import { Presets, VuePlugin } from "rete-vue-plugin";
import Vue from "vue";
const render = new VuePlugin({
setup(context) {
const app = new Vue({ ...context, yourPlugin });
return app;
},
});
```
where `yourPlugin` is an instance of any plugin (like [Vuetify](https://vuetifyjs.com/en/getting-started){rel="nofollow"} or [Vue I18N](https://kazupon.github.io/vue-i18n/){rel="nofollow"})
## Other presets
- [context menu](https://retejs.org/docs/guides/context-menu)
- [minimap](https://retejs.org/docs/guides/minimap)
- [reroute](https://retejs.org/docs/guides/reroute)
Check out the complete result on the [Customization for Vue.js](https://retejs.org/examples/customization/vue) example page.
# Angular
::alert
This guide is an extension of the [Basic](https://retejs.org/docs/guides/basic) guide and provides instructions for using the `rete-angular-plugin` instead of `rete-react-plugin`
::
::references
:::ref-example{link="/examples/basic/angular" title="Basic"}
:::
:::ref-example{link="/examples/customization/angular" title="Customization"}
:::
:::ref-example{link="/examples/controls/angular" title="Controls"}
:::
:::ref-github{link="https://github.com/retejs/angular-plugin" title="Plugin"}
:::
:::ref-external{link="https://angular.io/" title="Angular"}
:::
:::ref-guide{link="/docs/guides/context-menu" title="Context menu"}
:::
:::ref-guide{link="/docs/guides/minimap" title="Minimap"}
:::
:::ref-guide{link="/docs/guides/reroute" title="Reroute"}
:::
::
This plugin offers a classic preset that comes with visual components for nodes, connections, sockets, and input controls.
Compatible with Angular 12, 13, 14, 15, 16, 17, 18 and 19
This plugin is **exclusively** designed for Angular applications as it requires an `Injector` instance, unlike other render plugins. Additionally, the plugin supports Standalone mode in integration starting from Angular 19.
## Install dependencies
::kit
::
```bash
npm i rete-angular-plugin rete-render-utils @angular/elements@19
```
**Please note**: this plugin relies on `@angular/elements`, which is based on Web Components. However, Web Components have a limitation - they [cannot be unregistered](https://github.com/WICG/webcomponents/issues/754){rel="nofollow"}. This limitation may result in the reuse of the initial Angular component instead of creating a new one when a node with the same identifier is added, potentially leading to the use of outdated data within a custom node, such as data from an injected service.
## Plugin connection
This is an example of integration in **Angular 19**, but you can specify a different version (12, 13, 14, 15, 16, 17, 18 or 19) in the import that matches the version of your application.
These versions have been compiled with Ivy.
```ts
import { AreaPlugin } from "rete-area-plugin";
import { AngularPlugin, Presets, AngularArea2D } from "rete-angular-plugin/19";
type AreaExtra = AngularArea2D;
// ....
const render = new AngularPlugin({ injector });
render.addPreset(Presets.classic.setup());
area.use(render);
```
where `injector` is an instance of `Injector` that can be obtained through dependency injection (DI).
## Use Legacy View Engine
Additionally, the plugin provides support for the legacy engine which can be imported in the following way
```ts
import { AngularPlugin, Presets, AngularArea2D } from "rete-angular-plugin";
```
## Controls
This plugin provides built-in controls that are displayed based on the following objects:
- `ClassicPreset.InputControl` as `` or ``
Simply add the control to the node
```ts
node.addControl('my-control', new ClassicPreset.InputControl("number", {
initial: 0,
readonly: false,
change(value) { }
}))
```
If you want to add different types of controls, you can return the necessary component in the `control` handler of `customize` property.
```tsx
import { ControlComponent } from "rete-angular-plugin/19";
import { MyButtonComponent } from './my-button.component'
render.addPreset(Presets.classic.setup({
customize: {
control(context) {
if (context.payload.isButton) {
return MyButtonComponent
}
if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in ControlComponent
return ControlComponent
}
}
}
}));
node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
```
```ts
import { Component, Input } from "@angular/core";
@Component({
selector: "app-button",
template: ``
})
export class ButtonComponent {
@Input() data!: { label: string, onClick: () => void };
}
```
This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in [the example](https://retejs.org/examples/controls/angular)
Make sure to specify `(pointerdown)="$event.stopPropagation()"` to prevent the area from intercepting events such as `click`.
## Customization
In a similar manner to the approach outlined above, you can replace node, connection, or socket components.
### Customization of all nodes
If you want to completely change the node structure, you can implement your own component similar to [node](https://github.com/retejs/angular-plugin/blob/next/src/presets/classic/components/node){rel="nofollow"} from the classic preset
```ts
import { CustomNodeComponent } from './custom-node.component'
render.addPreset(Presets.classic.setup({
customize: {
node() {
return CustomNodeComponent
}
}
}))
```
The implementation of `CustomNodeComponent` is available in the **custom-node.component.ts** file of the [Customization for Angular](https://retejs.org/examples/customization/angular) example.
### Specific nodes
You can add an extra condition to apply this component only to specific nodes.
```ts
import { NodeComponent } from "rete-angular-plugin/19";
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "Custom") {
return CustomNodeComponent;
}
return NodeComponent; // use built-in component
}
}
}))
await editor.addNode(new ClassicPreset.Node('White'))
```
### Connection customization
Use **connection** as a starting point from the [presets/classic/components](https://github.com/retejs/angular-plugin/blob/next/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import { CustomConnectionComponent } from './custom-connection.component'
render.addPreset(Presets.classic.setup({
customize: {
connection() {
return CustomConnectionComponent
}
}
}))
```
### Socket customization
Use **socket** as a starting point from the [presets/classic/components](https://github.com/retejs/angular-plugin/blob/next/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import { CustomSocketComponent } from './custom-socket.component'
render.addPreset(Presets.classic.setup({
customize: {
socket() {
return CustomSocketComponent
}
}
}))
```
## Customize context menu
In order to customize the context menu for this rendering plugin, one can override styles using selectors (and it's important to consider the specificity of selectors in CSS)
```scss
[rete-context-menu] {
width: 320px;
context-menu-search input.search {
background: grey;
}
context-menu-item.block {
background: grey;
}
}
```
## Other presets
- [context menu](https://retejs.org/docs/guides/context-menu)
- [minimap](https://retejs.org/docs/guides/minimap)
- [reroute](https://retejs.org/docs/guides/reroute)
Check out the complete result on the [Customization for Angular](https://retejs.org/examples/customization/angular) example page.
# Svelte
::alert
This guide is an extension of the [Basic](https://retejs.org/docs/guides/basic) guide and provides instructions for using the `rete-svelte-plugin` instead of `rete-react-plugin`
::
::references
:::ref-example{link="/examples/basic/svelte" title="Basic"}
:::
:::ref-example{link="/examples/customization/svelte" title="Customization"}
:::
:::ref-example{link="/examples/controls/svelte" title="Controls"}
:::
:::ref-github{link="https://github.com/retejs/svelte-plugin" title="Plugin"}
:::
:::ref-external{link="https://svelte.dev/" title="Svelte"}
:::
:::ref-guide{link="/docs/guides/context-menu" title="Context menu"}
:::
:::ref-guide{link="/docs/guides/minimap" title="Minimap"}
:::
:::ref-guide{link="/docs/guides/reroute" title="Reroute"}
:::
::
This plugin offers a classic preset that comes with visual components for nodes, connections, sockets, and input controls.
Supports latest versions of Svelte: 5, 4 and 3
## Install dependencies
::kit
::
```bash
npm i rete-svelte-plugin rete-render-utils sass
```
## Plugin connection
```ts
import { AreaPlugin } from "rete-area-plugin";
import { SveltePlugin, Presets, SvelteArea2D } from "rete-svelte-plugin/5"; // or "rete-svelte-plugin" for older versions
type AreaExtra = SvelteArea2D;
// ....
const render = new SveltePlugin();
render.addPreset(Presets.classic.setup());
area.use(render);
```
Please note that this plugin is intended for client-side use only. Therefore, modules that use it within your `*.svelte` modules need to be dynamically imported.
```ts
onMount(async () => {
const { createEditor } = await import("./editor");
})
```
Check out the [Svelte](https://retejs.org/examples/basic/svelte) page for an example usage of this render plugin.
## Controls
This plugin provides built-in controls that are displayed based on the following objects:
- `ClassicPreset.InputControl` as `` or ``
Simply add the control to the node
```ts
node.addControl('my-control', new ClassicPreset.InputControl("number", {
initial: 0,
readonly: false,
change(value) { }
}))
```
If you want to add different types of controls, you can return the necessary component in the `control` handler of `customize` property.
```tsx
import MyButton from './MyButton.svelte'
render.addPreset(Presets.classic.setup({
customize: {
control(context) {
if (context.payload.isButton) {
return MyButton
}
if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in Presets.classic.Control
return Presets.classic.Control;
}
}
}
}));
node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
```
**MyButton.svelte**
```svelte
```
This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in [the example](https://retejs.org/examples/controls/svelte)
Make sure to specify `on:pointerdown|stopPropagation` to prevent the area from intercepting events such as `click`.
## Customization
In a similar manner to the approach outlined above, you can replace node, connection, or socket components.
### Customization of all nodes
If you want to completely change the node structure, you can implement your own component similar to [Node.svelte](https://github.com/retejs/svelte-plugin/blob/main/src/presets/classic/components/Node.svelte){rel="nofollow"} from the classic preset
```ts
import CustomNode from './CustomNode.svelte'
render.addPreset(Presets.classic.setup({
customize: {
node() {
return CustomNode
}
}
}))
```
The implementation of `CustomNode` is available in the **CustomNode.svelte** file of the [Customization for Svelte](https://retejs.org/examples/customization/svelte) example.
### Specific nodes
You can add an extra condition to apply this component only to specific nodes.
```ts
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "Custom") {
return CustomNode;
}
return Presets.classic.Node;
}
}
}))
await editor.addNode(new ClassicPreset.Node('White'))
```
### Connection customization
Use **Connection.svelte** as a starting point from the [presets/classic/components](https://github.com/retejs/svelte-plugin/blob/main/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import CustomConnection from './CustomConnection.svelte'
render.addPreset(Presets.classic.setup({
customize: {
connection() {
return CustomConnection
}
}
}))
```
### Socket customization
Use **Socket.svelte** as a starting point from the [presets/classic/components](https://github.com/retejs/svelte-plugin/blob/main/src/presets/classic/components){rel="nofollow"} directory of the plugin's source code.
```ts
import CustomSocket from './CustomSocket.svelte'
render.addPreset(Presets.classic.setup({
customize: {
socket() {
return CustomSocket
}
}
}))
```
## Customize context menu
In order to customize the context menu for this rendering plugin, one can override styles using selectors (and it's important to consider the specificity of selectors in CSS)
```scss
.rete-context-menu {
width: 320px !important;
.block:first-child input {
background: grey;
}
.block.item {
background: grey;
}
}
```
## Other presets
- [context menu](https://retejs.org/docs/guides/context-menu)
- [minimap](https://retejs.org/docs/guides/minimap)
- [reroute](https://retejs.org/docs/guides/reroute)
Check out the complete result on the [Customization for Svelte](https://retejs.org/examples/customization/svelte) example page.
# Lit
::alert
This guide is an extension of the [Basic](https://retejs.org/docs/guides/basic) guide and provides instructions for using the `@retejs/lit-plugin` instead of `rete-react-plugin`
::
::references
:::ref-example{link="/examples/basic/lit" title="Basic"}
:::
:::ref-example{link="/examples/customization/lit" title="Customization"}
:::
:::ref-example{link="/examples/controls/lit" title="Controls"}
:::
:::ref-github{link="https://github.com/retejs/lit-plugin" title="Plugin"}
:::
:::ref-external{link="https://lit.dev/" title="Lit"}
:::
:::ref-guide{link="/docs/guides/context-menu" title="Context menu"}
:::
:::ref-guide{link="/docs/guides/minimap" title="Minimap"}
:::
:::ref-guide{link="/docs/guides/reroute" title="Reroute"}
:::
::
This plugin offers a classic preset that comes with visual components for nodes, connections, sockets, and input controls.
Supports Lit version 3
## Install dependencies
::kit
::
```bash
npm i @retejs/lit-plugin rete-render-utils lit
```
## Plugin connection
```ts
import { AreaPlugin } from "rete-area-plugin";
import { LitPlugin, Presets, LitArea2D } from "@retejs/lit-plugin";
type AreaExtra = LitArea2D;
// ....
const render = new LitPlugin();
render.addPreset(Presets.classic.setup());
area.use(render);
```
Check out the [Lit](https://retejs.org/examples/basic/lit) page for an example usage of this render plugin.
## Controls
This plugin provides built-in controls that are displayed based on the following objects:
- `ClassicPreset.InputControl` as `` or ``
Simply add the control to the node
```ts
node.addControl('my-control', new ClassicPreset.InputControl("number", {
initial: 0,
readonly: false,
change(value) { }
}))
```
If you want to add different types of controls, you can return the necessary component in the `control` handler of `customize` property.
```ts
import { MyButtonElement } from './MyButton'
customElements.define("my-button", MyButtonElement);
render.addPreset(Presets.classic.setup({
customize: {
control(context) {
if (context.payload.isButton) {
const { payload } = context;
return () => html``;
}
if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in
return () => html``;
}
}
}
}));
node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
```
**MyButton.ts**
```ts
export class CustomButton extends LitElement {
static get properties() {
return {
data: { type: Object },
};
}
declare data: object;
render() {
return html`
`;
}
}
```
This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in [the example](https://retejs.org/examples/controls/lit)
Make sure to specify `@pointerdown` and `@doubleclick` to prevent the area from intercepting events such as `@click`.
## Customization
In a similar manner to the approach outlined above, you can replace node, connection, or socket components.
### Customization of all nodes
If you want to completely change the node structure, you can implement your own component similar to [node.ts](https://github.com/retejs/lit-plugin/blob/main/src/presets/classic/components/node.ts){rel="nofollow"} from the classic preset
```ts
import { CustomNodeElement } from './CustomNode'
customElements.define("custom-node", CustomNodeElement)
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
return ({ emit }) => html``;
}
}
}))
```
The implementation of `CustomNodeElement` is available in the **CustomNode.ts** file of the [Customization for Lit](https://retejs.org/examples/customization/lit) example.
### Specific nodes
You can add an extra condition to apply this component only to specific nodes.
```ts
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "Custom") {
return ({ emit }) => html``;
}
return ({ emit }) => html``;
}
}
}))
await editor.addNode(new ClassicPreset.Node('White'))
```
### Connection customization
Use **connection.ts** as a starting point from the [presets/classic/components](https://github.com/retejs/lit-plugin/blob/main/src/presets/classic/components/connection.ts){rel="nofollow"} directory of the plugin's source code.
```ts
import { CustomConnectionElement } from './CustomConnection'
customElements.define("custom-connection", CustomConnectionElement);
render.addPreset(Presets.classic.setup({
customize: {
connection() {
return ({ path }) => html``;
}
}
}))
```
### Socket customization
Use **socket.ts** as a starting point from the [presets/classic/components](https://github.com/retejs/lit-plugin/blob/main/src/presets/classic/components/socket.ts){rel="nofollow"} directory of the plugin's source code.
```ts
import { CustomSocketElement } from './CustomSocket'
customElements.define("custom-socket", CustomSocketElement);
render.addPreset(Presets.classic.setup({
customize: {
socket(context) {
return () => html``;
}
}
}))
```
## Other presets
- [context menu](https://retejs.org/docs/guides/context-menu)
- [minimap](https://retejs.org/docs/guides/minimap)
- [reroute](https://retejs.org/docs/guides/reroute)
Check out the complete result on the [Customization for Lit](https://retejs.org/examples/customization/lit) example page.
# Dataflow
::alert
Not familiar with Dataflow concept? Check out the [Dataflow](https://retejs.org/docs/concepts/engine#dataflow) article to get up to speed
::
::references
:::ref-example{link="/examples/processing/dataflow" title="Dataflow"}
:::
:::ref-example{link="/examples/3d-configurator" title="3D Configurator"}
:::
:::ref-example{link="/examples/allmatter" title="Allmatter"}
:::
:::ref-github{link="https://github.com/retejs/engine" title="Plugin"}
:::
::
## Install dependencies
::kit
::
```bash
npm i rete rete-engine
```
## Prepare nodes
Let's take a look at a simplified example of a graph with two node types: `NumberNode` and `AddNode`. These nodes are built exclusively for processing (on the server-side, e.g.) and don't have any integrations with the user interface. You can find a link to the complete example with the UI at the end of the article.
```ts
const socket = new ClassicPreset.Socket("socket");
class NumberNode extends ClassicPreset.Node {
constructor(public value: number) {
super("Number");
this.addOutput("value", new ClassicPreset.Output(socket, "Number"));
}
data(): { value: number } {
return { value: this.value };
}
}
class AddNode extends ClassicPreset.Node {
constructor() {
super("Add");
this.addInput("left", new ClassicPreset.Input(socket, "Left"));
this.addInput("right", new ClassicPreset.Input(socket, "Right"));
this.addOutput("value", new ClassicPreset.Output(socket, "Number"));
}
data(inputs: { left?: number[]; right?: number[] }): { value: number } {
const { left, right } = inputs;
const value = (left && left[0] || 0) + (right && right[0] || 0)
return { value };
}
}
class Connection<
A extends Node,
B extends Node
> extends ClassicPreset.Connection {}
type Node = NumberNode | AddNode;
type ConnProps = Connection | Connection;
type Schemes = GetSchemes;
```
## Connect
```ts
import { DataflowEngine } from "rete-engine";
import { NodeEditor } from "rete";
const editor = new NodeEditor();
const engine = new DataflowEngine();
editor.use(engine);
```
## Add nodes and connections
```ts
const a = new NumberNode(1);
const b = new NumberNode(1);
const sum = new AddNode();
const con1 = new Connection(a, "value", c, "left");
const con2 = new Connection(b, "value", c, "right");
await editor.addNode(a);
await editor.addNode(b);
await editor.addNode(sum);
await editor.addConnection(con1);
await editor.addConnection(con2);
```
## Start the processing
Retrieve output data from the `sum` node.
```ts
const result = await engine.fetch(sum.id)
```
The value of `result` will be `{ value: 2 }`, which is the sum of the initial input values of the `sum` node.
If you want to modify `a.value` or `b.value`, you need to clear the cache before processing the graph again. The output values of nodes are cached to avoid repetitive node execution.
```ts
engine.reset() // reset all nodes
// or specific nodes
engine.reset(a.id)
engine.reset(b.id)
```
Additionally, the `data` methods can be async. In such cases, the `sum` node will wait for the `data` methods of its input nodes to complete execution. After all of them have returned a value, the engine will execute the `data` method of the `sum` node
Check out the complete result on the [Dataflow](https://retejs.org/examples/processing/dataflow) example page.
# Control flow
::alert
Not familiar with Control flow concept? Check out the [Control flow](https://retejs.org/docs/concepts/engine#control-flow) article to get up to speed
::
::references
:::ref-example{link="/examples/processing/control-flow" title="Control flow"}
:::
:::ref-github{link="https://github.com/retejs/engine" title="Plugin"}
:::
::
## Install dependencies
::kit
::
```bash
npm i rete rete-engine
```
## Prepare nodes
Let's take a simple example of a graph with two types of nodes: `Log` and `Delay`. These nodes can perform specific operations and pass control to outgoing nodes in a certain way
At the end of the article, you can find a link to the full example that includes visual components.
Defining a node class that logs a message and passes control to outgoing nodes via the `exec` port:
```ts
const socket = new ClassicPreset.Socket("socket");
class Log extends ClassicPreset.Node {
constructor(public message: string) {
super("Log");
this.addInput("exec", new ClassicPreset.Input(socket, "Exec", true));
this.addOutput("exec", new ClassicPreset.Output(socket, "Exec"));
}
execute(input: "exec", forward: (output: "exec") => void) {
console.log(this.message);
forward("exec");
}
}
```
Defining a class that handles delays, where the only purpose is to pass control to outgoing nodes through the `exec` port after a specified timeout:
```ts
class Delay extends ClassicPreset.Node {
constructor(private seconds: number) {
super("Delay");
this.addInput("exec", new ClassicPreset.Input(socket, "Exec", true));
this.addOutput("exec", new ClassicPreset.Output(socket, "Exec"));
}
execute(input: "exec" | undefined, forward: (output: "exec") => void) {
setTimeout(() => forward("exec"), seconds * 1000)
}
}
class Connection extends ClassicPreset.Connection {}
type NodeProps = Start | Log | Delay;
type ConnProps =
| Connection
| Connection
| Connection
| Connection
| Connection;
type Schemes = GetSchemes;
```
## Connect
```ts
import { ControlFlowEngine } from "rete-engine";
import { NodeEditor } from "rete";
const editor = new NodeEditor();
const engine = new ControlFlowEngine();
editor.use(engine);
```
## Add nodes and connections
Let's add a sequence of nodes in the form of Log -> Delay -> Log
```ts
const log1 = new Log("log before delay");
const delay = new Delay(2);
const log2 = new Log("log after delay");
const con2 = new Connection(log1, "exec", delay, "exec");
const con3 = new Connection(delay, "exec", log2, "exec");
await editor.addNode(log1);
await editor.addNode(delay);
await editor.addNode(log2);
await editor.addConnection(con2);
await editor.addConnection(con3);
```
## Execution
The node `log1` serves as the starting point for the graph execution.
```ts
engine.execute(log1.id);
```
This operation triggers the `execute` method of the `Log` class, with `undefined` as the `input` parameter because the node was directly called, without being passed from an incoming node.
Then, calling `forward("exec")` passes control to all the outgoing nodes. In our case, the `Delay` node does the same thing but after a delay using `setTimeout`.
Logs:
```log
"log before delay"
// delay for 2 seconds
"log after delay"
```
Check out the complete result on the [Control flow](https://retejs.org/examples/processing/control-flow) example page.
# Hybrid Engine
::alert
For a brief overview of the concept of combining Dataflow and Control flow, we recommend reading the [Hybrid](https://retejs.org/docs/concepts/engine#hybrid) article
::
::references
:::ref-example{link="/examples/processing/hybrid-engine" title="Hybrid engine"}
:::
:::ref-example{link="/examples/chatbot" title="Chatbot"}
:::
:::ref-github{link="https://github.com/retejs/engine" title="Plugin"}
:::
:::ref-guide{link="/docs/guides/processing/dataflow" title="Dataflow"}
:::
:::ref-guide{link="/docs/guides/processing/control-flow" title="Control flow"}
:::
::
## Install dependencies
::kit
::
```bash
npm i rete rete-engine
```
## Prepare nodes
In order to work properly, all node classes must implement `execute` method for Control flow and `data` method for Dataflow.
The `Start` class is designed to pass control and has a default `data` method that returns an empty object.
```ts
const socket = new ClassicPreset.Socket("socket");
class Start extends ClassicPreset.Node {
constructor() {
super("Start");
this.addOutput("exec", new ClassicPreset.Output(socket, "Exec"));
}
execute(_: never, forward: (output: "exec") => void) {
forward("exec");
}
data() {
return {};
}
}
```
Along with receiving control, the `Log` class can also request data from the incoming nodes through the `message` port using the `fetchInputs` method of the `DataflowEngine` instance.
```ts
class Log extends ClassicPreset.Node {
constructor() {
super("Log");
this.addInput("exec", new ClassicPreset.Input(socket, "Exec", true));
this.addInput("message", new ClassicPreset.Input(socket, "Text"));
this.addOutput("exec", new ClassicPreset.Output(socket, "Exec"));
}
async execute(input: "exec", forward: (output: "exec") => void) {
const inputs = (await dataflow.fetchInputs(this.id)) as {
message: string[];
};
console.log((inputs.message && inputs.message[0]) || "");
forward("exec");
}
data() {
return {};
}
}
```
The `TextNode` class is responsible only for providing data and cannot receive or pass control.
```ts
class TextNode extends ClassicPreset.Node {
constructor(private text: string) {
super("Text");
this.addOutput("value", new ClassicPreset.Output(socket, "Number"));
}
execute() {}
data(): { value: string } {
return {
value: this.text
};
}
}
class Connection extends ClassicPreset.Connection {}
type NodeProps = Start | Log | TextNode;
type ConnProps = Connection | Connection;
type Schemes = GetSchemes;
```
## Connect
```ts
import { NodeEditor } from "rete";
import { DataflowEngine, ControlFlowEngine } from "rete-engine";
const editor = new NodeEditor();
const dataflow = new DataflowEngine(({ inputs, outputs }) => {
return {
inputs: () => Object.keys(inputs).filter((name) => name !== "exec"),
outputs: () => Object.keys(outputs).filter((name) => name !== "exec")
};
});
const controlflow = new ControlFlowEngine(() => {
return {
inputs: () => ["exec"],
outputs: () => ["exec"]
};
});
editor.use(dataflow);
editor.use(controlflow);
```
## Add nodes and connections
```ts
const start = new Start();
const text1 = new TextNode("log");
const log1 = new Log();
const con1 = new Connection(start, "exec", log1, "exec");
const con2 = new Connection(text1, "value", log1, "message");
await editor.addNode(start);
await editor.addNode(text1);
await editor.addNode(log1);
await editor.addConnection(con1);
await editor.addConnection(con2);
```
## Execution
The node `start` serves as the starting point for the graph execution.
```ts
engine.execute(start.id);
```
As a result, the `start` node passes control to the `log1` node, which fetches data from the `text1` node using the `fetchInputs` method.
Check out the complete result on the [Hybrid engine](https://retejs.org/examples/processing/hybrid-engine) example page. Additionally, you can explore another more sophisticated [Chatbot](https://retejs.org/examples/chatbot) example employing this approach.
# Codegen
::safety-tape{text="Under development"}
::
# 3D
::alert
This guide is based on the [Basic](https://retejs.org/docs/guides/basic) guide. It is recommended to review it for a comprehensive understanding of this guide.
::
::references
:::ref-example{link="/examples/3d" title="3D"}
:::
:::ref-example{link="/examples/3d/multiple-3d" title="Multiple 3D editors"}
:::
:::ref-external{link="https://threejs.org/" title="Three.js"}
:::
:::ref-github{link="https://github.com/retejs/area-3d-plugin" title="Plugin"}
:::
::
This guide outlines how to incorporate the node editor into a 3D scene. It can be achieved by `rete-area-3d-plugin`, which substitutes the standard `rete-area-plugin`.
::diagram{caption="Architecture" name="guides/3d/index"}
::
Powered by Three.js, this plugin creates a scene that contains a camera and two renderers - `CSS3DRenderer` and `WebGLRenderer`. Consequently, 3D objects and native HTML elements are rendered together in a single viewport, enabling overlapping. The primary advantage of utilizing native elements (as opposed to rendering to texture) is the preservation of full interactivity.
## Install dependencies
::kit
::
Prior to using this plugin, `three` peer dependency must be installed separately.
```bash
npm i rete-area-3d-plugin three @types/three
```
## Plugin connection
If you have followed the basic guide, you will need to replace the initialization of Area2DPlugin with Area3DPlugin and change some type definitions.
```ts
import { Area3D, Area3DPlugin } from 'rete-area-3d-plugin';
type AreaExtra =
| Area3D
const area = new Area3DPlugin(container);
editor.use(area);
```
Consequently, the container will incorporate both the `