# Svelte Flow Documentation What is Svelte Flow? Svelte Flow is a library that allows you to create interactive, node-based user interfaces: flowcharts, diagrams, visual programming tools, and workflows inside your svelte applications. It supports theming, custom nodes and edges, a library of shadcn UI components, and offers a large collection of examples for rapid development. Developers can leverage the Svelte Flow Pro platform for advanced features like real-time collaboration, complex layouts, and enhanced performance, making it suitable for both simple and large-scale, production-ready visual applications. ## Learn ### Quick Start This page will take you from zero to a working Svelte Flow app in a few minutes. From there, you can take a deeper look at what Svelte Flow is all about, check out the examples, or dive into the API docs. If you want to get up and running as soon as possible, you're in the right place! #### Installation First, spin up a new [Svelte](https://svelte.dev/) project however you like; we recommend using [Vite](https://vitejs.dev/) and [SvelteKit](https://svelte.dev/docs/kit/introduction) but the choice is yours. ```bash copy npm2yarn npx sv create my-svelte-flow-app ``` Then, navigate to your project directory and install the Svelte Flow package: ```bash copy npm2yarn npm install @xyflow/svelte ``` #### Usage The `@xyflow/svelte` package exports the `` component, which is the entrypoint for your flow. Importing the default styles and defining a handful of nodes and edges are all we need to get started! There are a few things to pay attention to here: * You must import the Svelte Flow stylesheet. * `` inherits the size of its parent. Wrap it in an element with dimensions. * Use [`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) instead of deeply reactive state for the `nodes` and `edges` for [performance reasons](https://github.com/sveltejs/svelte/issues/11851). ```svelte
``` #### Result Et voila. You've already created your first interactive flow! Example: learn/quickstart ##### App.svelte ```svelte
``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Next steps ### Server Side Rendering ### Server side rendering, server side generation This is an advanced use case and assumes you are already familiar with SvelteFlow. If you're new to SvelteFlow, check out our [getting started guide](/learn/concepts/terms-and-definitions). In this guide, you'll learn how to configure SvelteFlow for server-side rendering, enabling you to: * Generate static HTML diagrams for documentation * Render SvelteFlow diagrams in non-JavaScript environments * Create dynamic Open Graph images for social media sharing (For client-side image generation, check out our [download image example](/examples/misc/download-image).) ##### Why Server-Side Rendering is Complex To understand why server-side rendering in Svelte Flow requires special configuration, let's look at what SvelteFlow typically handles on the client side: 1. **Node Dimension Calculation** * Nodes can contain any content, so their dimensions are determined by the browser's layout engine * This dynamic sizing isn't available during server-side rendering 2. **Handle Position Detection** * Edge connections require precise handle positions * These positions are calculated based on CSS layout, which isn't available on the server 3. **Container Size Adaptation** * SvelteFlow adapts to its container's dimensions * Server-side rendering needs explicit dimensions ##### Node Dimensions The most crucial aspect of server-side rendering is specifying node dimensions. On the client, SvelteFlow automatically measures nodes and stores dimensions in `measured.width` and `measured.height`. For server-side rendering, you must provide these dimensions explicitly using either: Node Dimension Options: 1. `width` and `height`: Static dimensions that won't change
2. `initialWidth` and `initialHeight`: Dynamic dimensions that may change after client-side hydration
```svelte ``` ##### Handle Positions To render edges on the server, you need to provide handle positions explicitly. On the client, SvelteFlow calculates these positions automatically, but for server-side rendering, you must specify them using the `handles` property: ```svelte ``` ##### Using `fitView` with Server-Side Rendering If you know your container's dimensions, you can use `fitView` during server-side rendering by providing the container's width and height: ```svelte ``` ##### Generating Static HTML To create static HTML output, you can use Svelte's server-side rendering capabilities. This generates an HTML string that you can use for static files or HTTP responses: ```svelte ``` ### Usage with TypeScript Svelte Flow is written in TypeScript because we value the additional safety barrier it provides. We export all the types you need for correctly typing data structures and functions you pass to the Svelte Flow component. We also provide a way to extend the types of nodes and edges. #### Basic Usage Let's start with the essential types needed for a basic implementation. While TypeScript can infer some types automatically, we'll define them explicitly for clarity. ```svelte ``` ##### Custom Nodes When working with [custom nodes](/learn/customization/custom-nodes), you can extend the base `Node` type to include your custom data. There are two main approaches: 1. For **multiple custom nodes**, specify a custom `Node` type as a generic to `NodeProps`: ```svelte
A special number: {data.number}
``` ⚠️ When defining node data separately, you must use `type` (interfaces won't work): ```ts type NumberNodeData = { number: number }; type NumberNodeType = Node; ``` 2. For **a single custom node** that renders different content based on the node type, use a union type: ```svelte
{#if data.type === 'number'}
A special number: {data.number}
{:else}
A special text: {data.text}
{/if}
``` ##### Custom Edges Similar to custom nodes, you can extend the base `Edge` type for [custom edges](/learn/customization/custom-edges): ```svelte ``` #### Advanced Usage In complex applications, you'll likely have multiple custom nodes and edges with different data structures. When using built-in functions and hooks, you'll need to properly [narrow down](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) the types to prevent runtime errors. ##### `Node` and `Edge` Type Unions Many functions, callbacks, and hooks (including the SvelteFlow component) expect `NodeType` or `EdgeType` generics. These are unions of all your custom node and edge types. As long as you've properly typed your data objects, you can use their exported types. If you're using any built-in nodes ('input', 'output', 'default') or edges ('straight', 'step', 'smoothstep', 'bezier'), include the `BuiltInNode` and `BuiltInEdge` types from `@xyflow/svelte` in your union type. ```svelte ``` ##### Hooks You can use these type unions to properly type the return values of hooks: ```svelte ``` ##### Type Guards TypeScript provides several ways to implement [type guards](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#typeof-type-guards). One common approach is to create type guard functions like `isNumberNode` or `isTextNode` to filter specific nodes from a list: ```ts function isNumberNode(node: NodeType): node is NumberNodeType { return node.type === 'number'; } // numberNodes is now correctly typed as NumberNodeType[] let numberNodes = $derived(nodes.filter(isNumberNode)); ``` ### Building a Flow In the following pages we will introduce you to the core concepts of Svelte Flow and explain how to create a basic interactive flow. A flow consists of [nodes](/api-reference/types/node), [edges](/api-reference/types/edge) and the viewport. If you haven't reviewed our [Key Concepts](/learn/concepts/terms-and-definitions) yet, we recommend doing that first. To follow along with this guide you will need to have a Svelte project set up and install the `@xyflow/svelte` package: ```bash copy npm2yarn npm install @xyflow/svelte ``` #### Creating the flow Let's start by creating an empty flow with viewport [``](/api-reference/components/controls) and a dotted [``](/api-reference/components/background). ### Add imports First, import the Svelte Flow Component and its required styles into your project. We'll also import the `Background` component for visual enhancement, and the `Controls` component for the viewport controls. ```svelte ``` ##### Render SvelteFlow Next, render the main component inside an element with defined dimensions and place the [``](/api-reference/components/background) and [``](/api-reference/components/controls) components inside `SvelteFlow`. Content inside `SvelteFlow` stays fixed on top of the viewport. The `Background` component transforms its pattern to match viewport movement. ```svelte
``` ##### Empty flow That's it! You have created your first empty flow πŸŽ‰ If everything is set up correctly, you should see a blank canvas like this: Example: learn/building-a-flow-1 ##### App.svelte ```svelte
``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ```
#### Adding nodes Now that the flow is set up, let's add some nodes - each node represents an element in your diagram with a specific position and content. ### Create node objects Outside of your Svelte component, in the `
``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ```
We have several built-in nodes that you can explore in the [node](/api-reference/types/node) reference. However, once you start building your own application, you will want to use [custom nodes](/learn/customization/custom-nodes). #### Adding edges Now that we have two nodes, let’s connect them with an edge. ### Create edge objects To create an edge, we define an array of [edge](/api-reference/types/edge) objects. Each edge object needs to have an `id`, a `source` (where the edge begins), and a `target` (where it ends). In this example, we use the `id` values of the two nodes we created so far (`n1` and `n2`) to define the edge: We use the [`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) rune to make the array reactive. ```js let edges = $state.raw([ { id: 'n1-n2', source: 'n1', target: 'n2' }, ]); ``` ##### Add edges to the flow As with nodes, we [bind](https://svelte.dev/docs/svelte/$bindable) the edges to the `SvelteFlow` component. ```svelte "bind:edges" ``` Your flow should now look like this: Example: learn/building-a-flow-3 ##### App.svelte ```svelte
``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### Edge label and type By default, Svelte Flow allows you to add text labels to edges that will float in the middle of the edge via the `label` property. Also, many types of edges are built in, such as `smoothstep` and `step`. We can change the type of the edge by setting the `type` property. ```js {6-7} let edges = $state.raw([ { id: 'n1-n2', source: 'n1', target: 'n2', type: 'smoothstep', label: 'connects with', }, ]); ``` ##### Done! πŸŽ‰ Congratulations! You have completed a basic flow with nodes and edges! πŸŽ‰
#### Full code example 🏁 You took your first steps in Svelte Flow! The completed flow will look like this: Example: learn/building-a-flow-4 ##### App.svelte ```svelte
``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } /* This is just here to better match the design of svelteflow.dev */ .svelte-flow { --xy-background-color: #f7f9fb; --xy-edge-label-background-color-default: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ### Built-In Components Svelte Flow comes with several additional components that you can plug into your flow. All you have to do is import and add them as children to the `SvelteFlow` component. ```svelte filename="BuiltInComponents.svelte"

My Flow

``` #### MiniMap The [`MiniMap`](/api-reference/components/mini-map) provides a bird's-eye view of your flowgraph, making navigation easier, especially for larger flows. You can customize the appearance of nodes in the minimap by providing a `nodeColor` function. Example: learn/minimap ##### App.svelte ```svelte { switch (node.type) { case 'input': return '#6ede87'; case 'output': return '#6865A5'; default: return '#ff0072'; } }} zoomable pannable /> ``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } ``` ##### index.html ```html SvelteFlow MiniMap Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Controls Svelte Flow comes with a set of customizable [`Controls`](/api-reference/components/controls) for the viewport. You can zoom in and out, fit the viewport and toggle if the user can move, select and edit the nodes. Example: learn/controls ##### App.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } ``` ##### index.html ```html SvelteFlow Controls Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Background The [`Background`](/api-reference/components/background) component adds a visual grid pattern to your flowgraph, helping users maintain orientation. You can choose from different pattern variants, or if you need more advanced customization, you can explore the [source code](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Background/Background.svelte) to implement your own pattern. Example: learn/background ##### App.svelte ```svelte
variant:
``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html SvelteFlow Background Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Panel The [`Panel`](/api-reference/components/panel) component allows you to add fixed overlays to your flowgraph, perfect for titles, controls, or any other UI elements that should remain stationary. Example: learn/panel ##### App.svelte ```svelte top-left top-center top-right bottom-left bottom-center bottom-right center-left center-right ``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } .svelte-flow { --xy-background-color: #f7f9fb; } .svelte-flow__panel { padding: 5px 10px; background: white; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; } ``` ##### index.html ```html SvelteFlow Panel Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ### Overview At its core, Svelte Flow is about creating interactive flowgraphs - a collection of nodes connected by edges. While this might sound simple in theory, Svelte Flow provides the foundation for building complex interactive diagrams: * An infinite, interactive canvas for your flowgraph * The ability to render and connect nodes with edges * **Everything is built using standard Svelte components** To help you understand the terminology we use throughout the documentation, take a look at the example flow below. Example: learn/basic-terms ##### AnnotationNode.svelte ```svelte
{data.label}
{#if data.arrowStyle}
{/if} ``` ##### App.svelte ```svelte ``` ##### ButtonEdge.svelte ```svelte ``` ##### CircleNode.svelte ```svelte
{label || 'no node connected'}
``` ##### Flow\.svelte ```svelte
x: {viewport.current.x.toFixed(2)}
y: {viewport.current.y.toFixed(2)}
zoom: {viewport.current.zoom.toFixed(2)}
``` ##### Message.svelte ```svelte
On the bottom left you see the Controls and the bottom right the{' '} MiniMap. This is also just a node πŸ₯³
``` ##### NodeWrapper.svelte ```svelte
{@render children?.()}
``` ##### ResizerNode.svelte ```svelte
{data.label}
``` ##### TextNode.svelte ```svelte {#each ['width', 'height'] as attr} {/each} {#if dimensions === null} no node connected {/if} ``` ##### ToolbarNode.svelte ```svelte {#each emojis as emoji (emoji)} {/each}
{selectedEmoji}
{data.label}
``` ##### xy-theme.css ```css /* xyflow theme files. Delete these to start from our base */ .svelte-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #ff4000; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://svelteflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-resize-background-color-default: #9e86ed; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .svelte-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: 0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3), 0px 1px 2px 0px rgba(0, 0, 0, 0.35); --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .svelte-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .svelte-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .svelte-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .svelte-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node.selectable:hover, .svelte-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .svelte-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .svelte-flow.dark .svelte-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .svelte-flow__edge.selectable:hover .svelte-flow__edge-path, .svelte-flow__edge.selectable.selected .svelte-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .svelte-flow__handle { background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:focus, .svelte-flow__handle.connectingfrom, .svelte-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .svelte-flow__node:has(.svelte-flow__resize-control.line) { border-radius: 0; } .svelte-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: var(--xy-background-color); color: var(--xy-edge-label-color-default); } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff4000; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } /* Custom Button Styling */ .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } .xy-theme__button { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; box-sizing: border-box; } #app { width: 100vw; height: 100vh; } /* Circle Node */ .svelte-flow__node-circle { border-radius: 50%; height: 100px; width: 100px; font-family: monospace; text-align: center; } /* Text Input Node */ .svelte-flow__node-textinput { width: 150px; font-family: monospace; text-align: left; } .text-input-node__input { width: 100%; box-sizing: border-box; margin: 5px 0px; border-radius: 3px; border: var(--xy-node-border-default); } .text-input-node__input::-webkit-outer-spin-button, input[type='number']::-webkit-inner-spin-button { -webkit-appearance: none; } .text-input-node__input { -moz-appearance: textfield; appearance: textfield; } /* Annotation Node */ .svelte-flow__node-annotation { font-size: 16px; width: 250px; color: #683bfa; position: absolute; box-shadow: none; font-family: monospace; text-align: left; background-color: transparent; border: none; pointer-events: none; } .svelte-flow__node-annotation .annotation-content { padding: 10px; display: flex; } .svelte-flow__node-annotation .annotation-level { margin-right: 4px; } .svelte-flow__node-annotation .annotation-arrow { position: absolute; font-size: 24px; } /* Toolbar Node */ .svelte-flow__node-toolbar { background-color: #000000; border-radius: 16px; overflow: hidden; } .svelte-flow__node-toolbar button { cursor: pointer; background: inherit; border: none; padding: 5px 7px; margin: 3px; border-radius: 50%; box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-toolbar button:hover { background: #4d4d4d; } /* Resizer Node */ .resizer-node__handles { display: flex; position: absolute; bottom: 0; width: 100%; justify-content: space-evenly; left: 0; } .resizer-node__handle { position: relative; left: 0; transform: none; } /* Button Edge */ .button-edge__label { position: absolute; pointer-events: all; transform-origin: center; background: transparent; } .button-edge__button { width: 30px; height: 30px; border: 5px solid #f7f9fb; color: var(--xy-edge-node-color-default); background-color: #f3f3f4; cursor: pointer; border-radius: 50%; font-size: 12px; padding-top: 0px; } .button-edge__button:hover { background-color: var(--xy-theme-hover); color: #ffffff; } /* Custom Handles */ .svelte-flow__handle.custom-handle { background-color: var(--xy-handle-border-color-default); border-radius: 1px; width: 8px; height: 4px; border: none; min-width: 2px; min-height: 2px; } .svelte-flow__handle.custom-handle:hover, .svelte-flow__handle.custom-handle.connectionindicator:focus, .svelte-flow__handle.custom-handle.connectingfrom, .svelte-flow__handle.custom-handle.connectingto { background-color: var(--xy-theme-edge-hover); } .svelte-flow__handle-bottom.custom-handle { bottom: -5px; transform: none; } .svelte-flow__handle-top.custom-handle { top: -5px; transform: none; } .svelte-flow__handle-left.custom-handle { height: 8px; width: 4px; left: -3px; } /* Minimap */ .svelte-flow__minimap .group { fill-opacity: 0.4; } .svelte-flow__minimap .resizer, .svelte-flow__minimap .tools, .svelte-flow__minimap .circle, .svelte-flow__minimap .textinput { fill: rgb(208, 192, 247); } .svelte-flow__minimap .circle { rx: 100%; ry: 100%; } .svelte-flow__minimap .annotation { display: none; } .viewport-logger { font-family: monospace; color: var(--xy-node-color-default); padding: 5px; border-radius: 3px; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### nodes-and-edges.ts ```ts import { MarkerType, type Node, type Edge } from '@xyflow/svelte'; import { type BuiltInNode } from '@xyflow/svelte'; import { type AnnotationNode } from './AnnotationNode.svelte'; import { type CircleNode } from './CircleNode.svelte'; export type AppNode = BuiltInNode | AnnotationNode | CircleNode; export const initialNodes: Node[] = [ { id: 'annotation-1', type: 'annotation', draggable: false, selectable: false, data: { label: 'this is a "node"', arrowStyle: 'right: 0; bottom: 0; transform: translate(-30px,10px) rotate(-70deg);', }, position: { x: -80, y: -50 }, }, { id: '1-1', type: 'input', data: { label: 'node label', }, position: { x: 150, y: 0 }, }, { id: 'annotation-2', type: 'annotation', draggable: false, selectable: false, data: { label: 'this is a "handle"', arrowStyle: 'left: 0; bottom: 0; transform: translate(0px, -25px) rotate(140deg) scale(-1, 1);', }, position: { x: 230, y: 30 }, }, { id: 'annotation-3', type: 'annotation', draggable: false, selectable: false, data: { level: 2, label: 'this is an "edge"', arrowStyle: 'right: 0; bottom: 0; transform: translate(-10px, -25px) rotate(190deg);', }, position: { x: 20, y: 120 }, }, { id: '1-2', type: 'default', data: { label: 'node label', }, position: { x: 350, y: 200 }, }, { id: 'annotation-4', type: 'annotation', draggable: false, selectable: false, data: { label: 'try dragging the handle', arrowStyle: 'left: 0; bottom: 0; transform: translate(-15px, -25px) rotate(140deg) scale(-1, 1);', }, position: { x: 450, y: 220 }, }, ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1-1', target: '1-2', label: 'edge label', type: 'smoothstep', }, { id: 'e2-2', source: '1-2', target: '2-2', type: 'smoothstep', markerEnd: { type: MarkerType.ArrowClosed, }, }, ]; ``` ##### Nodes Svelte Flow has a few default node types out of the box, but customization is where the magic of Svelte Flow truly happens. You can design your nodes to work exactly the way you need them toβ€”whether that’s embedding interactive form elements, displaying dynamic data visualizations, or even incorporating multiple connection handles. Svelte Flow lays the foundation, and your imagination does the rest. We have a guide on creating your own [Custom Nodes](/learn/customization/custom-nodes) and you can find all the options for customizing your nodes in the [Node](/api-reference/types/node) reference. ##### Handles A handle (also known as a β€œport” in other libraries) is the attachment point where an edge connects to a node. By default, they appear as grey circles on the top, bottom, left, or right sides of a node. But they are just `div` elements, and can be positioned and styled any way you’d like. When creating a custom node, you can include as many handles as needed. For more information, refer to the [Handle](/learn/customization/handles) page. ##### Edges Edges connect nodes. Every edge needs a target and a source node. Svelte Flow comes with four built-in [edge types](/examples/edges/edge-types): `default` (bezier), `smoothstep`, `step`, and `straight`. An edge is an SVG path that can be styled with CSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node. Like custom nodes, you can also customize edges. Things that people do with custom edges include: * Adding buttons to remove edges * Custom routing behavior * Complex styling or interactions that cannot be solved with just one SVG path For more information, refer to the [Edges](/learn/customization/custom-edges) page. ##### Selection You can select an edge or a node by clicking on it. If you want to select multiple nodes/edges via clicking, you can hold the `Meta/Control` key and click on multiple elements to select them. If you want to change the keyboard key for multiselection to something else, you can use the [`multiSelectionKey`](/api-reference/svelte-flow#multiselectionkey) prop. You can also select multiple edges/nodes by holding down `Shift` and dragging the mouse to make a selection box. When you release the mouse, any node or edge that falls within the selection box is selected. If you want to change the keyboard key for this behavior, you can use the [`selectionKey`](/api-reference/svelte-flow#selectionkey) prop. Selected nodes and edges are elevated (assigned a higher `zIndex` than other elements), so that they are shown on top of all the other elements. For default edges and nodes, selection is shown by a darker stroke/border than usual. If you are using a custom node/edge, you can use the `selected` prop to customize selection appearance inside your custom component. ##### Connection line Svelte Flow has built-in functionality that allows you to click and drag from one handle to another to create a new edge. While dragging, the placeholder edge is referred to as a connection line. The connection line comes with the same four built-in types as edges and is customizable. You can find the props for configuring the connection line in the [connection props](/api-reference/svelte-flow#connection-line-props) reference. ##### Viewport All of Svelte Flow is contained within the viewport. Each node has an x- and y-coordinate, which indicates its position within the viewport. The viewport has x, y, and zoom values. When you drag the pane, you change the x and y coordinates. When you zoom in or out, you alter the zoom level. You can read more about the viewport in the [Viewport](/learn/concepts/the-viewport) page. ### Panning and Zooming The default pan and zoom behavior of Svelte Flow is inspired by [slippy maps](https://wiki.openstreetmap.org/wiki/Slippy_map). You pan by dragging your pointer and zoom by scrolling. You can customize this behavior easily with the [interaction](/api-reference/svelte-flow#interaction-props) and [keyboard](/api-reference/svelte-flow#keyboard-props) props on the `` component. #### Viewport configurations Here we will list and explain some configurations that other tools use. ##### Default viewport controls As mentioned above, the `` default controls are as follows: * `pan:` pointer drag * `zoom:` pinch or scroll * `select:` shift + pointer drag Example: learn/zoom-pan ##### App.svelte ```svelte ``` ##### xy-theme.css ```css /* xyflow theme files. Delete these to start from our base */ .svelte-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #ff4000; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://svelteflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-resize-background-color-default: #9e86ed; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .svelte-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: 0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3), 0px 1px 2px 0px rgba(0, 0, 0, 0.35); --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .svelte-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .svelte-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .svelte-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .svelte-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node.selectable:hover, .svelte-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .svelte-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .svelte-flow.dark .svelte-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .svelte-flow__edge.selectable:hover .svelte-flow__edge-path, .svelte-flow__edge.selectable.selected .svelte-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .svelte-flow__handle { background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:focus, .svelte-flow__handle.connectingfrom, .svelte-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .svelte-flow__node:has(.svelte-flow__resize-control.line) { border-radius: 0; } .svelte-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: var(--xy-background-color); color: var(--xy-edge-label-color-default); } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff4000; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } /* Custom Button Styling */ .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } .xy-theme__button { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### Design tool viewport controls If you prefer figma/sketch/design tool controls you can set [`panOnScroll`](/api-reference/svelte-flow#panonscroll) and [`selectionOnDrag`](/api-reference/svelte-flow#selectionondrag) to `true` and [`panOnDrag`](/api-reference/svelte-flow#panondrag) to `false`: * `pan:` scroll, middle / right mouse drag, space + pointer drag * `zoom:` pinch or cmd + scroll * `select:` pointer drag Example: learn/zoom-pan-2 ##### App.svelte ```svelte ``` ##### xy-theme.css ```css /* xyflow theme files. Delete these to start from our base */ .svelte-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #ff4000; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://svelteflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-resize-background-color-default: #9e86ed; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .svelte-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: 0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3), 0px 1px 2px 0px rgba(0, 0, 0, 0.35); --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .svelte-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .svelte-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .svelte-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .svelte-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node.selectable:hover, .svelte-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .svelte-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .svelte-flow.dark .svelte-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .svelte-flow__edge.selectable:hover .svelte-flow__edge-path, .svelte-flow__edge.selectable.selected .svelte-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .svelte-flow__handle { background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:focus, .svelte-flow__handle.connectingfrom, .svelte-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .svelte-flow__node:has(.svelte-flow__resize-control.line) { border-radius: 0; } .svelte-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: var(--xy-background-color); color: var(--xy-edge-label-color-default); } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff4000; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } /* Custom Button Styling */ .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } .xy-theme__button { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` In this example we also set `selectionMode="partial"` to be able to add nodes to a selection that are only partially selected. ### Custom Edges Like [custom nodes](/learn/customization/custom-nodes), parts of a custom edge in Svelte Flow are just Svelte components: that means you can render anything you want along an edge! This guide shows you how to implement a custom edge with some additional controls. For a comprehensive reference of props available for custom edges, see the [`EdgeProps`](/api-reference/types/edge-props) documentation. #### A basic custom edge An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the [``](/api-reference/components/base-edge) component. To calculate the actual SVG path to render, Svelte Flow comes with some handy utility functions: * [`getBezierPath`](/api-reference/utils/get-bezier-path) * [`getSmoothStepPath`](/api-reference/utils/get-smooth-step-path) * [`getStraightPath`](/api-reference/utils/get-straight-path) To kick start our custom edge, we'll just render a straight path between the source and target. ```svelte filename="CustomEdge.svelte" ``` All the props passed to your custom edge component can be found in the API reference under the [`EdgeProps`](/api-reference/types/edge-props) type. This gives us a straight edge that behaves the same as the default `"straight"` [edge type](/api-reference/types/edge#default-edge-types). To use it, we also need to update the [`edgeTypes`](/api-reference/svelte-flow#edge-types) prop on the `` component. It's important to define the `edgeTypes` object *outside of the component* or to use Svelte's `$derived` to prevent unnecessary re-renders. Svelte Flow will show a warning in the console if you forget to do this. ```svelte filename="App.svelte" ``` After defining the `edgeTypes` object, we can use our new custom edge by setting the `type` field of an edge to `"custom-edge"`. Example: learn/custom-edge ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { margin: 0; font-family: sans-serif; width: 100%; height: 100%; } /* This is just here to better match the design of svelteflow.dev */ .svelte-flow { --xy-background-color: #f7f9fb; --xy-edge-label-background-color-default: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Making Custom SVG Edge Paths As discussed previously, if you want to make a custom edge in Svelte Flow, you have to use either of the four path creation functions discussed above (e.g [`getBezierPath`](/api-reference/utils/get-bezier-path)). However if you want to make some other path shape like a Sinusoidal edge or some other edge type then you will have to make the edge path yourself. The edge path we get from functions like [`getBezierPath`](/api-reference/utils/get-bezier-path) is just a path string which we pass into the `path` prop of the `` component. It contains the necessary information needed in order to draw that path, like where it should start from, where it should curve, where it should end, etc. A simple straight path string between two points `(x1, y1)` to `(x2, y2)` would look like: ```svelte M x1 y1 L x2 y2 ``` An SVG path is a concatenated list of commands like `M`, `L`, `Q`, etc, along with their values. Some of these commands are listed below, along with their supported values. * `M x1 y1` is the Move To command which moves the current point to the x1, y1 coordinate. * `L x1 y1` is the Line To command which draws a line from the current point to x1, y1 coordinate. * `Q x1 y1 x2 y2` is the Quadratic Bezier Curve command which draws a bezier curve from the current point to the x2, y2 coordinate. x1, y1 is the control point of the curve which determines the curviness of the curve. Whenever we want to start a path for our custom edge, we use the `M` command to move our current point to `sourceX, sourceY` which we get as props in the custom edge component. Then based on the shape we want, we will use other commands like `L`(to make lines), `Q`(to make curves) and then finally end our path at `targetX, targetY` which we get as props in the custom edge component. If you want to learn more about SVG paths, you can check out [SVG-Path-Editor](https://yqnn.github.io/svg-path-editor/). You can paste any SVG path there and analyze individual path commands via an intuitive UI. Here is an example with two types of custom edge paths, a Step edge and a Sinusoidal edge. You should look at the Step edge first to get your hands dirty with custom SVG paths since it's simple, and then look at how the Sinusoidal edge is made. After going through this example, you will have the necessary knowledge to make custom SVG paths for your custom edges. Example: learn/custom-edge-path ##### App.svelte ```svelte ``` ##### SineEdge.svelte ```svelte ``` ##### StepEdge.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { margin: 0; font-family: sans-serif; width: 100%; height: 100%; } /* This is just here to better match the design of svelteflow.dev */ .svelte-flow { --xy-background-color: #f7f9fb; --xy-edge-label-background-color-default: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ### Custom Nodes A powerful feature of Svelte Flow is the ability to create custom nodes. This gives you the flexibility to render anything you want within your nodes. We generally recommend creating your own custom nodes rather than relying on built-in ones. With custom nodes, you can add as many source and target handles as you likeβ€”or even embed form inputs, charts, and other interactive elements. In this section, we'll walk through creating a custom node featuring an input field that updates text elsewhere in your application. For further examples, we recommend checking out our [Custom Node Example](/examples/nodes/custom-node). #### Implementing a Custom Node To create a custom node, all you need to do is create a Svelte component. Svelte Flow will automatically wrap it in an interactive container that injects essential props like the node's id, position, and data, and provides functionality for selection, dragging, and connecting handles. For a full reference on all available custom node props, take a look at the [Node Props](/api-reference/types/node-props). Let's dive into an example by creating a custom node called `TextUpdaterNode`. For this, we've added a controlled input field with a oninput handler. We simply use the 'text' property from the node's data for the input and we update the node's data via the [`updateNodeData`](/api-reference/hooks/use-svelte-flow#update-node-data) function, that can be accessed through the [`useSvelteFlow`](/api-reference/hooks/use-svelte-flow) hook. ```svelte filename="TextUpdaterNode.svelte"
{ updateNodeData(id, { text: evt.target.value }); }} class="nodrag" />
``` #### Adding the Node Type Now we need to communicate the new custom node to Svelte Flow. You can add custom nodes by passing the [`nodeTypes`](/api-reference/svelte-flow#node-types) prop. ```svelte filename="App.svelte" ``` After defining your new node type, you can refer to it by using the `type` node option: ```js let nodes = $state.raw([ { id: 'node-1', type: 'textUpdater', position: { x: 0, y: 0 }, data: { text: 'some text' }, }, ]); ``` After putting it all together and adding some basic styles we get a custom node that prints text to the console: Example: learn/custom-node ##### App.svelte ```svelte ``` ##### TextUpdaterNode.svelte ```svelte
{ updateNodeData(id, { text: evt.target.value }); }} class="nodrag" />
``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } .svelte-flow { --xy-background-color: #f7f9fb; } .text-updater-node { height: 50px; padding: 5px; background: var(--xy-node-background-color-default); } .text-updater-node label { display: block; color: var(--xy-node-color-default); font-size: 12px; } .text-updater-node input { color: var(--xy-node-color-default); background: var(--xy-node-background-color-default); /* border: 1px solid var(--xy-node-border-color-default); */ border-radius: 3px; padding: 5px; font-size: 12px; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` To enable your custom node to connect with other nodes, check out the [Handles](/learn/customization/handles) page to learn how to add source and target handles. ### Edge Labels One of the more common uses for custom edges is rendering some controls or info along an edge's path. In Svelte Flow we call that an *edge label* and unlike the edge path, edge labels can be any Svelte component! #### Adding an edge label Because Svelte Flow's edges are mounted inside an SVG component, we need to escape its context to render a custom edge label. For this, we have a handy [``](/api-reference/components/edge-label) component. Aside from a couple of extras, like inheriting the edges z-index, it functions as a portal that mounts the child components in the viewport div. Let's add a button to our custom edge that can be used to delete the edge it's attached to: ```svelte filename="CustomEdge.svelte" ``` To make sure our edge labels are interactive and not just for presentation, it is important to add the `nodrag` and `nopan` classes to the label to stop mouse events from controlling the canvas. Here's an interactive example with our updated custom edge. Clicking the delete button will remove that edge from the flow. Creating a new edge will use the custom node. Example: learn/custom-edge-2 ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body, #app { margin: 0; font-family: sans-serif; width: 100%; height: 100%; } /* This is just here to better match the design of svelteflow.dev */ .svelte-flow { --xy-background-color: #f7f9fb; --xy-edge-label-background-color-default: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ### Handles Handles are the connection points on [nodes](/learn/concepts/terms-and-definitions#nodes) in Svelte Flow. Our built-in nodes include one source and one target handle, but you can customize your nodes with as many different handles as you need. #### Creating a node with handles To create a [custom node](/learn/customization/custom-nodes) with handles, you can use the [``](/api-reference/components/handle) component provided by Svelte Flow. This component allows you to define source and target handles for your custom nodes. Here's an example of how to implement a custom node with two handles: ```svelte {7-8}
Custom Node Content
``` #### Using multiple handles If you want to use multiple source or target handles in your custom node, you need to specify each handle with a unique `id`. This allows Svelte Flow to differentiate between the handles when connecting edges. ```svelte /id="a"/ /id="b"/ ``` To connect an edge to a specific handle of a node, use the properties `sourceHandle` (for the edge's starting point) and `targetHandle` (for the edge's ending point). By defining `sourceHandle` or `targetHandle` with the appropriate handle `id`, you instruct Svelte Flow to attach the edge to that specific handle, ensuring that connections are made where you intend. ```js "sourceHandle: 'a'" "sourceHandle: 'b'" const initialEdges = [ { id: 'n1-n2', source: 'n1', sourceHandle: 'a', target: 'n2' }, { id: 'n1-n3', source: 'n1', sourceHandle: 'b', target: 'n3' }, ]; ``` In this case, the source node is `n1` for both handles but the handle `id`s are different. One comes from handle id `a` and the other one from `b`. Both edges also have different target nodes: Example: learn/custom-node-2 ##### App.svelte ```svelte ``` ##### TextUpdaterNode.svelte ```svelte
Custom Node
``` ##### xy-theme.css ```css /* xyflow theme files. Delete these to start from our base */ .svelte-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #ff4000; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://svelteflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-resize-background-color-default: #9e86ed; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .svelte-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: 0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3), 0px 1px 2px 0px rgba(0, 0, 0, 0.35); --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .svelte-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .svelte-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .svelte-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .svelte-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node.selectable:hover, .svelte-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .svelte-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .svelte-flow.dark .svelte-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .svelte-flow__edge.selectable:hover .svelte-flow__edge-path, .svelte-flow__edge.selectable.selected .svelte-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .svelte-flow__handle { background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:focus, .svelte-flow__handle.connectingfrom, .svelte-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .svelte-flow__node:has(.svelte-flow__resize-control.line) { border-radius: 0; } .svelte-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: var(--xy-background-color); color: var(--xy-edge-label-color-default); } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff4000; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } /* Custom Button Styling */ .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } .xy-theme__button { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` By default Svelte Flow positions a handle in the center of the specified side. If you want to display multiple handles on a side, you can adjust the position via inline styles or overwrite the default CSS and position it on your own. #### Custom handles You can use any custom component as a Handle by wrapping it with the [``](/api-reference/components/handle) component. Follow these steps to integrate your custom component: 1. Wrap your custom component with [``](/api-reference/components/handle) component. 2. Hide the built-in [``](/api-reference/components/handle) appearance by setting `border` and `background` to `none`. 3. Set the `width` and `height` of [``](/api-reference/components/handle) to match your custom component. 4. Style the child component with `pointer-events: none`. 5. Then, reposition the child custom component using CSS position properties like `top, left` if necessary to position it perfectly inside the [``](/api-reference/components/handle) component. This example shows a custom icon used as a Handle: ```svelte
Custom Node
``` #### Typeless handles If you want to create a handle that does not have a specific type (source or target), you can set [connectionMode](/api-reference/svelte-flow#connectionmode) to `Loose` in the `` component. This allows the handle to be used for both incoming and outgoing connections. #### Dynamic handles If you are programmatically changing the position or number of handles in your custom node, you need to update the node internals with the [`useUpdateNodeInternals`](/api-reference/hooks/use-update-node-internals) hook. #### Custom handle styles Since the handle is a div, you can use CSS to style it or pass a style prop to customize a Handle. You can see this in the [Add Node On Edge Drop](/examples/nodes/add-node-on-edge-drop) example. ##### Styling handles when connecting The handle receives the additional class names `connectingfrom` and `connectingto` when the connection line is above the handle and `valid` if the connection is valid. You can find an example which uses these classes [here](/examples/interaction/validation). ##### Hiding handles If you need to hide a handle for some reason, you must use `visibility: hidden` or `opacity: 0` instead of `display: none`. This is important because Svelte Flow needs to calculate the dimensions of the handle to work properly and using `display: none` will report a width and height of `0`! ### Theming Svelte Flow comes with minimal default styles and was designed to be fully customizable. Many of our users fully transform the look and feel of Svelte Flow to match their own brand or design system. This guide will introduce you to the different ways you can customize Svelte Flow's appearance. #### Default styles Svelte Flow's default styles are enough to get going with the built-in nodes. They provide some sensible defaults for styles like padding, border radius, and animated edges. You can see what they look like below: ```js import '@xyflow/svelte/dist/style.css'; ``` Example: learn/theming-default ##### App.svelte ```svelte ``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Base styles If you just want to load the very basic styles that are necessary for Svelte Flow to work, you can import the base styles instead: ```js import '@xyflow/svelte/dist/base.css'; ``` These base styles are **required** for Svelte Flow to function correctly. If you don't expected! Example: learn/theming-base ##### App.svelte ```svelte ``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` #### Customization There are different ways how you can customize the appearance of Svelte Flow: * Work with scoped CSS within your custom nodes and edges * Override the built-in classes with custom CSS * Override the CSS variables Svelte Flow uses * Pass inline styles through `style` props ##### Working with built-in classes If you want to override the default styles, you can do so by overriding the built-in CSS classes. You can find a list of all the classes used by Svelte Flow below: Be careful if you go poking around the source code looking for other classes to override. Some classes are used internally and are required in order for the library to be functional. If you replace them you may end up with unexpected bugs or errors! ##### Built in dark and light mode You can choose one of the built-in color modes by using the `colorMode` prop ('dark', 'light' or 'system') as seen in the [dark mode example](/examples/styling/dark-mode). ```svelte import { SvelteFlow } from '@xyflow/svelte'; ``` When you use the `colorMode` prop, Svelte Flow adds a class to the root element (`.svelte-flow`) that you can use to style your flow based on the color mode: ```css .dark .svelte-flow__node { background: #777; color: white; } .light .svelte-flow__node { background: white; color: #111; } ``` ##### CSS variables If you don't want to replace the default styles entirely but just want to tweak the overall look and feel, you can override some of the CSS variables we use throughout the library. These variables are mostly self-explanatory. Below is a table of all the variables you might want to tweak and their default values for reference: These variables are used to define the *defaults* for the various elements of Svelte Flow. This means they can still be overridden by inline styles or custom classes on a per-element basis. If you want to override a variable, you can do so by adding: ```css .svelte-flow { --xy-node-background-color-default: #ff5050; } ``` ##### TailwindCSS Custom nodes and edges are just Svelte components, and you can use any styling solution you'd like to style them. For example, you might want to use [Tailwind](https://tailwindcss.com/) to style your nodes: ```svelte
{data.emoji}
{data.name}
{data.job}
``` If you want to overwrite default styles, make sure to import Tailwinds entry last! ```js import '@xyflow/svelte/dist/style.css'; import 'tailwind.css'; ``` For a complete example of using Tailwind with Svelte Flow, check out the [tailwind example](/examples/styling/tailwind)! ### Utility Classes Svelte Flow provides several built-in utility CSS classes to help you fine-tune how interactions work within your custom elements. #### `nodrag` Adding the class `nodrag` to an element ensures that interacting with it doesn't trigger a drag. This is particularly useful for elements like buttons or inputs that should not initiate a drag operation when clicked. Nodes have a `drag` class name in place by default. However, this class name can affect the behaviour of the event listeners inside your custom nodes. To prevent unexpected behaviours, add a `nodrag` class name to elements with an event listener. This prevents the default drag behavior as well as the default node selection behavior when elements with this class are clicked. ```svelte
``` #### `nopan` If an element in the canvas does not stop mouse events from propagating, clicking and dragging that element will pan the viewport. Adding the `nopan` class prevents this behavior. You can change the class name with the [`noPanClass`](/api-reference/svelte-flow#style-props) prop on ``. ```svelte

fixed content...

``` #### `nowheel` If your custom element contains scrollable content, you can apply the `nowheel` class. This disables the canvas' default pan behavior when you scroll inside your custom node, ensuring that only the content scrolls instead of moving the entire canvas. You can change the class name with the [`noWheelClass`](/api-reference/svelte-flow#style-props) prop on ``. ```svelte

Scrollable content...

``` Applying these utility classes helps you control interaction on a granular level. You can customize these class names on `` via the [style props](/api-reference/svelte-flow#style-props). When creating your own custom nodes, you will also need to remember to style them! Unlike the built-in nodes, custom nodes have no default styles, so feel free to use any styling method you prefer, such as [Tailwind CSS](/examples/styling/tailwind). ### Overview We regularly get asked how to handle layouting in Svelte Flow. While we could build some basic layouting into Svelte Flow, we believe that **you know your app's requirements best** and with so many options out there we think it's better you choose the best right tool for the job (not to mention it'd be a whole bunch of work for us). To start let's put together a simple example flow that we can use as a base for testing out the different layouting options. Example: learn/layouting-flow-1-empty ##### App.svelte ```svelte ``` ##### LayoutFlow\.svelte ```svelte ``` ##### xy-theme.css ```css /* xyflow theme files. Delete these to start from our base */ .svelte-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #ff4000; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://svelteflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-resize-background-color-default: #9e86ed; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .svelte-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: 0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3), 0px 1px 2px 0px rgba(0, 0, 0, 0.35); --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .svelte-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .svelte-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .svelte-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .svelte-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node.selectable:hover, .svelte-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .svelte-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .svelte-flow.dark .svelte-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .svelte-flow__edge.selectable:hover .svelte-flow__edge-path, .svelte-flow__edge.selectable.selected .svelte-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .svelte-flow__handle { background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:focus, .svelte-flow__handle.connectingfrom, .svelte-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .svelte-flow__node:has(.svelte-flow__resize-control.line) { border-radius: 0; } .svelte-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: var(--xy-background-color); color: var(--xy-edge-label-color-default); } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff4000; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } /* Custom Button Styling */ .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } .xy-theme__button { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### nodes-edges.js ```js export const initialNodes = [ { id: '1', type: 'input', data: { label: 'input' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'node 2' }, position: { x: 0, y: 100 }, }, { id: '2a', data: { label: 'node 2a' }, position: { x: 0, y: 200 }, }, { id: '2b', data: { label: 'node 2b' }, position: { x: 0, y: 300 }, }, { id: '2c', data: { label: 'node 2c' }, position: { x: 0, y: 400 }, }, { id: '2d', data: { label: 'node 2d' }, position: { x: 0, y: 500 }, }, { id: '3', data: { label: 'node 3' }, position: { x: 200, y: 100 }, }, ]; export const initialEdges = [ { id: 'e12', source: '1', target: '2', animated: true }, { id: 'e13', source: '1', target: '3', animated: true }, { id: 'e22a', source: '2', target: '2a', animated: true }, { id: 'e22b', source: '2', target: '2b', animated: true }, { id: 'e22c', source: '2', target: '2c', animated: true }, { id: 'e2c2d', source: '2c', target: '2d', animated: true }, ]; ``` Each of the examples that follow will be built on this empty flow. Where possible we've tried to keep the examples confined to just one `index.js` file so it's easy for you to compare how they're set up. #### Layouting Nodes For layouting nodes, there are a few third-party libraries that we think are worth checking out: ΒΉ Dagre currently has an [open issue](https://github.com/dagrejs/dagre/issues/238) that prevents it from laying out sub-flows correctly if any nodes in the sub-flow are connected to nodes outside the sub-flow. We've loosely ordered these options from simplest to most complex, where dagre is largely a drop-in solution and elkjs is a full-blown highly configurable layouting engine. Below, we'll take a look at a brief example of how each of these libraries can be used with Svelte Flow. For dagre and elkjs specifically, we have some separate examples you can refer back to [here](/examples/layout/dagre) and [here](/examples/layout/elkjs). ##### Dagre * Repo: * Docs: Dagre is a simple library for layouting directed graphs. It has minimal configuration options and a focus on speed over choosing the most optimal layout. If you need to organize your flows into a tree, *we highly recommend dagre*. Example: learn/layouting-flow-2-dagre ##### App.svelte ```svelte ``` ##### Flow\.svelte ```svelte ``` ##### index.css ```css html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } ``` ##### index.html ```html SvelteFlow Dagre Layout Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### nodes-edges.js ```js export const initialNodes = [ { id: '1', type: 'input', data: { label: 'input' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'node 2' }, position: { x: 0, y: 100 }, }, { id: '2a', data: { label: 'node 2a' }, position: { x: 0, y: 200 }, }, { id: '2b', data: { label: 'node 2b' }, position: { x: 0, y: 300 }, }, { id: '2c', data: { label: 'node 2c' }, position: { x: 0, y: 400 }, }, { id: '2d', data: { label: 'node 2d' }, position: { x: 0, y: 500 }, }, { id: '3', data: { label: 'node 3' }, position: { x: 200, y: 100 }, }, ]; export const initialEdges = [ { id: 'e12', source: '1', target: '2', animated: true }, { id: 'e13', source: '1', target: '3', animated: true }, { id: 'e22a', source: '2', target: '2a', animated: true }, { id: 'e22b', source: '2', target: '2b', animated: true }, { id: 'e22c', source: '2', target: '2c', animated: true }, { id: 'e2c2d', source: '2c', target: '2d', animated: true }, ]; ``` With no effort at all we get a well-organized tree layout! Whenever `getLayoutedElements` is called, we'll reset the dagre graph and set the graph's direction (either left-to-right or top-to-bottom) based on the `direction` prop. Dagre needs to know the dimensions of each node in order to lay them out, so we iterate over our list of nodes and add them to dagre's internal graph. After laying out the graph, we'll return an object with the layouted nodes and edges. We do this by mapping over the original list of nodes and updating each node's position according to node stored in the dagre graph. Documentation for dagre's configuration options can be found [here](https://github.com/dagrejs/dagre/wiki#configuring-the-layout), including properties to set for spacing and alignment. ##### D3-Hierarchy * Repo: * Docs: When you know your graph is a tree with a single root node, d3-hierarchy can provide a handful of interesting layouting options. While the library can layout a simple tree just fine, it also has layouting algorithms for tree maps, partition layouts, and enclosure diagrams. Example: learn/layouting-flow-3-d3-hierarchy ##### App.svelte ```svelte ``` ##### Flow\.svelte ```svelte ``` ##### index.css ```css html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } ``` ##### index.html ```html SvelteFlow MiniMap Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### nodes-edges.js ```js export const initialNodes = [ { id: '1', type: 'input', data: { label: 'input' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'node 2' }, position: { x: 0, y: 100 }, }, { id: '2a', data: { label: 'node 2a' }, position: { x: 0, y: 200 }, }, { id: '2b', data: { label: 'node 2b' }, position: { x: 0, y: 300 }, }, { id: '2c', data: { label: 'node 2c' }, position: { x: 0, y: 400 }, }, { id: '2d', data: { label: 'node 2d' }, position: { x: 0, y: 500 }, }, { id: '3', data: { label: 'node 3' }, position: { x: 200, y: 100 }, }, ]; export const initialEdges = [ { id: 'e12', source: '1', target: '2', animated: true }, { id: 'e13', source: '1', target: '3', animated: true }, { id: 'e22a', source: '2', target: '2a', animated: true }, { id: 'e22b', source: '2', target: '2b', animated: true }, { id: 'e22c', source: '2', target: '2c', animated: true }, { id: 'e2c2d', source: '2c', target: '2d', animated: true }, ]; ``` D3-hierarchy expects your graphs to have a single root node, so it won't work in all cases. It's also important to note that d3-hierarchy assigns the same width and height to _all_ nodes when calculating the layout, so it's not the best choice if you're displaying lots of different node types. ##### D3-Force * Repo: * Docs: For something more interesting than a tree, a force-directed layout might be the way to go. D3-Force is a physics-based layouting library that can be used to position nodes by applying different forces to them. As a consequence, it's a little more complicated to configure and use compared to dagre and d3-hierarchy. Importantly, d3-force's layouting algorithm is iterative, so we need a way to keep computing the layout across multiple renders. First, let's see what it does: Example: learn/layouting-flow-4-d3-force ##### App.svelte ```svelte ``` ##### Flow\.svelte ```svelte ``` ##### collide.js ```js import { quadtree } from 'd3-quadtree'; export function collide() { let nodes = []; let force = (alpha) => { const tree = quadtree( nodes, (d) => d.x, (d) => d.y, ); for (const node of nodes) { const r = node.measured.width / 2; const nx1 = node.x - r; const nx2 = node.x + r; const ny1 = node.y - r; const ny2 = node.y + r; tree.visit((quad, x1, y1, x2, y2) => { if (!quad.length) { do { if (quad.data !== node) { const r = node.measured.width / 2 + quad.data.width / 2; let x = node.x - quad.data.x; let y = node.y - quad.data.y; let l = Math.hypot(x, y); if (l < r) { l = ((l - r) / l) * alpha; node.x -= x *= l; node.y -= y *= l; quad.data.x += x; quad.data.y += y; } } } while ((quad = quad.next)); } return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; }); } }; force.initialize = (newNodes) => (nodes = newNodes); return force; } export default collide; ``` ##### index.css ```css html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } ``` ##### index.html ```html SvelteFlow MiniMap Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### nodes-edges.js ```js export const initialNodes = [ { id: '1', type: 'input', data: { label: 'input' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'node 2' }, position: { x: 0, y: 100 }, }, { id: '2a', data: { label: 'node 2a' }, position: { x: 0, y: 200 }, }, { id: '2b', data: { label: 'node 2b' }, position: { x: 0, y: 300 }, }, { id: '2c', data: { label: 'node 2c' }, position: { x: 0, y: 400 }, }, { id: '2d', data: { label: 'node 2d' }, position: { x: 0, y: 500 }, }, { id: '3', data: { label: 'node 3' }, position: { x: 200, y: 100 }, }, ]; export const initialEdges = [ { id: 'e12', source: '1', target: '2', animated: true }, { id: 'e13', source: '1', target: '3', animated: true }, { id: 'e22a', source: '2', target: '2a', animated: true }, { id: 'e22b', source: '2', target: '2b', animated: true }, { id: 'e22c', source: '2', target: '2c', animated: true }, { id: 'e2c2d', source: '2c', target: '2d', animated: true }, ]; ``` In our Svelte implementation, we use Svelte's lifecycle hooks and reactive state to manage the force simulation. The simulation is configured with a number of different forces applied so you can see how they interact: play around in your own code to see how you want to configure those forces. You can find the documentation and some different examples of d3-force [here](https://d3js.org/d3-force). Rectangular collisions D3-Force has a built-in collision force, but it assumes nodes are circles. We've thrown together a custom force in `collision.js` that uses a similar algorithm but accounts for our rectangular nodes instead. Feel free to steal it or let us know if you have any suggestions for improvements! The tick function progresses the simulation by one step and then updates Svelte Flow with the new node positions. We've also included a demonstration on how to handle node dragging while the simulation is running: if your flow isn't interactive you can ignore that part! For larger graphs, computing the force layout every render forever is going to incur a big performance hit. In this example we have a simple toggle to turn the layouting on and off, but you might want to come up with some other approach to only compute the layout when necessary. ##### Elkjs * Repo: * Docs: (good luck!) Elkjs is certainly the most configurable option available, but it's also the most complicated. Elkjs is a Java library that's been ported to JavaScript, and it provides a huge number of options for configuring the layout of your graph. Example: learn/layouting-flow-6-elkjs ##### App.svelte ```svelte ``` ##### Flow\.svelte ```svelte
``` ##### index.css ```css html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } ``` ##### index.html ```html SvelteFlow MiniMap Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### nodes-edges.js ```js export const initialNodes = [ { id: '1', type: 'input', data: { label: 'input' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'node 2' }, position: { x: 0, y: 100 }, }, { id: '2a', data: { label: 'node 2a' }, position: { x: 0, y: 200 }, }, { id: '2b', data: { label: 'node 2b' }, position: { x: 0, y: 300 }, }, { id: '2c', data: { label: 'node 2c' }, position: { x: 0, y: 400 }, }, { id: '2d', data: { label: 'node 2d' }, position: { x: 0, y: 500 }, }, { id: '3', data: { label: 'node 3' }, position: { x: 200, y: 100 }, }, ]; export const initialEdges = [ { id: 'e12', source: '1', target: '2', animated: true }, { id: 'e13', source: '1', target: '3', animated: true }, { id: 'e22a', source: '2', target: '2a', animated: true }, { id: 'e22b', source: '2', target: '2b', animated: true }, { id: 'e22c', source: '2', target: '2c', animated: true }, { id: 'e2c2d', source: '2c', target: '2d', animated: true }, ]; ``` At its most basic we can compute layouts similar to dagre, but because the layouting algorithm runs asynchronously we need to handle promises and update our Svelte state accordingly. The ELK reference is your new best friend We don't often recommend elkjs because its complexity makes it difficult for us to support folks when they need it. If you do decide to use it, you'll want to keep the original [Java API reference](https://eclipse.dev/elk/reference.html) handy. We've also included a few examples of some of the other layouting algorithms available, including a non-interactive force layout. ##### Honourable Mentions Of course, we can't go through every layouting library out there: we'd never work on anything else! Here are some other libraries we've come across that might be worth taking a look at: * If you want to use dagre or d3-hierarchy but need to support nodes with different dimensions, both [d3-flextree](https://github.com/klortho/d3-flextree) and [entitree-flex](https://github.com/codeledge/entitree-flex) look promising. * [Cola.js](https://github.com/tgdwyer/WebCola) looks like a promising option for so-called "constraint-based" layouts. We haven't had time to properly investigate it yet, but it looks like you can achieve results similar to d3-force but with a lot more control. #### Routing Edges If you don't have any requirements for edge routing, you can use one of the layouting libraries above to position nodes and let the edges fall wherever they may. Otherwise, you'll want to look into some libraries and techniques for edge routing. Your options here are more limited than for node layouting, but here are some resources we thought looked promising: * [Routing Orthogonal Diagram Connectors in JavaScript](https://medium.com/swlh/routing-orthogonal-diagram-connectors-in-javascript-191dc2c5ff70) If you do explore some custom edge routing options, consider contributing back to the community by writing a blog post or creating a library! You can use the editable edges functionality in Svelte Flow as a starting point for implementing a custom edge that can be routed along a specific path. ### Sub Flows A sub flow is a flowgraph contained within a node. These nested flows can operate independently or connect with nodes outside their parent node, making them perfect for organizing and grouping related nodes. This guide will show you how to implement sub flows and explain the available options for child nodes. ##### Defining Child Nodes To define a child node, use the `parentId` option (see all available options in the [node options section](/api-reference/types/node)). Child nodes are positioned relative to their parent, with `{ x: 0, y: 0 }` representing the top-left corner of the parent node. ```js highlight="parentId: 'A'" let nodes = $state.raw([ { id: 'A', data: { label: 'parent' }, position: { x: 0, y: 0 }, }, { id: 'B', data: { label: 'child' }, position: { x: 10, y: 10 }, parentId: 'A', }, ]); ``` Order of Nodes: Parents have to appear before their child nodes in the `nodes` array! ###### `extent: 'parent'` When a parent node has defined dimensions, you can restrict child node movement to stay within the parent's boundaries by setting `extent: 'parent'`. This prevents child nodes from being dragged outside their parent's area. Example: learn/sub-flows ##### App.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } html, body, #app { width: 100%; height: 100%; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### Child Node Behavior Child nodes maintain their relative position when the parent node is moved. While the `parentId` option establishes the parent-child relationship, child nodes can still be positioned outside their parent (unless `extent: 'parent'` is set). However, they will always move with their parent when it's dragged. In our examples, we use the built-in `group` node type for parent nodes, which provides a transparent background with a border. You can also use [any custom node type](/learn/layouting/sub-flows#using-a-custom-parent-node) as a parent. Let's explore more complex scenarios by adding additional nodes and edges. You can create connections both within a group and between sub flows and outer nodes: Example: learn/sub-flows-2 ##### App.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } html, body, #app { width: 100%; height: 100%; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ##### Using a Custom Parent Node To demonstrate the flexibility of parent nodes, let's modify our example by removing the label from node B and adding child nodes. This example shows how you can use any node type as a parent. We'll also set `draggable: false` on the child nodes to make them static. Example: learn/sub-flows-3 ##### App.svelte ```svelte ``` ##### index.css ```css @import './xy-theme.css'; html, body { margin: 0; font-family: sans-serif; } html, body, #app { width: 100%; height: 100%; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### index.html ```html Svelte Flow Example
``` ##### index.ts ```ts import { mount } from 'svelte'; import App from './App.svelte'; import './index.css'; mount(App, { target: document.getElementById('app')!, }); ``` ### Migrate to v1 ### Migrate to Svelte Flow 1.0 Svelte Flow 1.0 is built from the ground up with Svelte 5 and includes many new features and improvements. This guide will help you migrate from Svelte Flow 0.x to 1.0. If you are looking for the Svelte Flow 0.x docs, please refer to [legacy.svelteflow.dev](https://legacy.svelteflow.dev). #### New features * **[Reconnect edges](/examples/edges/reconnect-edge)**: You can reconnect your edges by using the new `` component. It can be used to add custom reconnection points on custom edges. * **Keyboard navigation & A11y**: We added support for keyboard navigation and improved accessibility for screen readers. You can now tab through nodes and edges and move nodes with the arrow keys. Can be disabled via [**disableKeyboardA11y**](/api-reference/svelte-flow#disablekeyboarda11y) * **[Click connect](/examples/edges/click-connect)**: You can now create a new connection by clicking on a handle one by one. * **[Enhanced ViewportPortal](/api-reference/components/viewport-portal)**: You can now decide if you want to render something below or above the nodes & edges in the viewport. * **Improved [fitView](/api-reference/hooks/use-svelte-flow#fitview)**: We finetuned the `fitView` function to better work with dynamically added nodes. * **colorModeSSR** prop: You can pass a fallback color mode for server side rendering when colorMode is set to 'system'. * [**elevateNodesOnSelect**](/api-reference/svelte-flow#elevateNodesOnSelect) & [**elevateEdgesOnSelect**](/api-reference/svelte-flow#elevateEdgesOnSelect): Control if nodes & edges should be elevated via z-index when selected. * [**noDragClass, noWheelClass, noPanClass**](/api-reference/svelte-flow#style-props): You can now modify the class name used to disable dragging, panning and zooming. * [**onselectionchange**](/api-reference/svelte-flow#onselectionchange) & [**useOnSelectionChange**](/api-reference/hooks/use-on-selection-change): You can now listen to selection changes via a callback #### Breaking changes ##### `nodes` & `edges` are now using `$state.raw` instead of `writable` Svelte 5 introduces runes which are now getting used for nodes and edges. **Old API** ```js const nodes = writable([...]); const edges = writable([...]); ``` **New API** ```js let nodes = $state.raw([...]); let edges = $state.raw([...]); ``` ##### Updating Nodes & Edges Previously it was possible to update single node properties. Theoretically, this would also be possible with `$state`, however the [performance implications](https://svelte.dev/playground/e6f804ba6da348bc8b6a0a13c59672cb?version=5.19.0) of this are unfortunately too great, so we opted to using `$state.raw`. This means that `nodes` and `edges` are to be treated as immutable from now on. If you are making updates manually make sure you: 1. create a new node/edge object, when updating a property. 2. reassign the nodes/edges array (this was technically required before anyway) ```js nodes[0].position.x = 100; // won't work const newNode = { ...nodes[0] }; newNode.position.x = 100; nodes[0] = newNode; // not enough to trigger an update nodes = [...nodes]; // this will make it work nodes = nodes.map((node) => { if (node.id === '1') { return { ...node, position: { ...node.position, x: 100 } }; } return node; }); // also works updateNode('1', (node) => ({ ...node, position: { ...node.position, x: 100 }, })); // using the updateNode helper from useSvelteFlow ``` ##### `nodes` & `edges` need to be bound from `` **Old API** ```js ``` **New API** ```js ``` If `nodes` and `edges` live in a separate module, you can use [function bindings](https://svelte.dev/docs/svelte/bind#Function-bindings). ```js // store.svelte.js let nodes = $state.raw([...]); let edges = $state.raw([...]); export const getNodes = () => nodes; export const getEdges = () => edges; export const setNodes = (newNodes) => nodes = newNodes; export const setEdges = (newEdges) => edges = newEdges; ``` ```js // BaseComponent.svelte ``` ##### Custom Node & Edge Props This is by enlarge a general change in Svelte 5, but it does have quite a big impact on typing the props of Custom Nodes & Edges. **Old API** ```js // CustomNode.svelte type $$Props = NodeProps; export let data: $$Props['data']; export let position: $$Props['position']; export let selected: $$Props['selected']; ``` **New API** ```js let { data, position, selected } : NodeProps = $props(); ``` ##### Hooks Hooks now return reactive values instead of writables. Because `$state` values cannot be [returned by functions directly](https://svelte.dev/docs/svelte/$state#Passing-state-into-functions) we have to return an object with a `.current` property to keep reactivity. In this regard, we are [following the official trend](https://svelte.dev/docs/svelte/svelte-reactivity#MediaQuery) set by the Svelte library authors. **Old API** ```js const edges = useEdges(); $: console.log(edges); ``` **New API** ```js const edges = useEdges(); $inspect(edges.current); ``` Note that in case of `useNodes`, `useEdges` and `useViewport` reassignments to `.current` work! ```js const nodes = useNodes(); function updateNodes() { nodes.current = [...] } ``` ##### Binding the viewport Binding the viewport now works natively in Svelte 5. You can either access the internal viewport or bind your very own viewport object to be used instead. **Old API** ```js const viewport = writable({ x: 100, y: 100, zoom: 1.25 }); ``` **New API** ```js let viewport = $state < Viewport > { x: 100, y: 100, zoom: 1.25 }; ; ``` ##### Custom Connection Line Using a custom Connection Line was possible before by passing it to a [slot](https://svelte.dev/docs/svelte/legacy-slots). In Svelte Flow 1.0 we introduced a new prop called `connectionLineComponent`for this. **Old API** ```js ``` **New API** ```js ``` ##### `onEdgeCreate` becomes `onbeforeconnect` `onedgecreate` was called before a new edge was created. This is now called `onbeforeconnect` to better align with events like [`onbeforeconnect`](/api-reference/svelte-flow#onbeforedelete). **Old API** ```js ({...connection, id: crypto.randomUUID()})} /> ``` **New API** ```js ({ ...connection, id: crypto.randomUUID() })} /> ``` ##### `` becomes [``](/api-reference/components/edge-label) The `EdgeLabelRenderer` component is now called `EdgeLabel`. As it was just a simple Portal to begin with, the naming of it being a "renderer" was a bit misleading. To add to this, the new `EdgeLabel` component now also handles clicks on the label automatically and is aware of what edge it belongs to. **Old API** ```js filename="CustomEdge.svelte"
My Edge Label
``` **New API** ```js filename="CustomEdge.svelte"
My Edge Label
``` ### Remove Attribution If you’re considering removing the attribution, we’d first like to mention: **If you’re using Svelte Flow at your organization and making money from it**, we rely on your support to keep Svelte Flow developed and maintained under an MIT License. Before you remove the attribution, [see the ways you can support Svelte Flow to keep it running](/pro). **Are you using Svelte Flow for a personal project?** Great! Go ahead and remove the attribution. You can support us by reporting any bugs you find, sending us screenshots of your projects, and starring us on [Github](https://github.com/xyflow/xyflow). If you start making money using Svelte Flow or use it in an organization in the future, we would ask that you re-add the attribution or become a Github or Open Collective Sponsor. Thank you for supporting us ✌🏻 * [the xyflow team](https://xyflow.com/about) To remove our attribution in the corner of your application you can pass `hideAttribution` through `proOptions` prop to the `` component. ```svelte filename="App.svelte" ``` ## Examples ## API Reference ### API Reference This reference attempts to document every function, hook, component, and type exported by Svelte Flow. If you are looking for guides, please refer to our [learn section](/learn). #### How to use this reference We think that documentation should answer two broad questions: "what is this thing?" and "how do I use it?" To that end, our API reference aims to **concisely** answer that first question, while guides go into more detail on the second. If you find yourself clicking around the reference wondering what the heck any of this means, maybe we have a guide that can help you out! #### A note for JavaScript users Svelte Flow is written in TypeScript, but we know that not everyone uses it. We encourage developers to use the technology that works best for them, and throughout our documentation there is a blend of TypeScript and JavaScript examples. For our API reference, however, we use TypeScript's syntax to document the types of props and functions. Here's a quick crash course on how to read it: β€’ `?` means that the field or argument is optional. β€’ `` in a type definition represents a generic type parameter. Like a function argument but for types! The definition `type Array = ...` means a type called `Array` that takes a generic type parameter `T`. β€’ `` when referring to a type is like "filling in" a generic type parameter. It's like calling a function but for types! The type `Array` is the type `Array` with the generic type parameter `T` filled in with the type `number`. β€’ `T | U` means that the type is either `T` or `U`: this is often called a *union*. β€’ `T & U` means that the type is both `T` and `U`: this is often called an *intersection*. The TypeScript folks have their own [handy guide for reading types](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) that you might find useful. If you're still stuck on something, feel free to drop by our [Discord](https://discord.com/invite/RVmnytFmGW) and ask for help! ### The SvelteFlowProvider component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/SvelteFlowProvider/SvelteFlowProvider.svelte) The `` component wraps its child nodes with a Svelte context that makes it possible to access a flow's internal state outside of the [``](/api-reference/svelte-flow) component. Many of the hooks we provide rely on this component to work. ```svelte filename="App.svelte" ``` ```svelte filename="Sidebar.svelte" ``` The state provided by `` is first initialized with default values. Only after the `` component initializes, will the state be replaced with correct values. However, you can expect this to happen before the first render. #### Notes * If you're using a router and want your flow's state to persist across routes, it's vital that you place the `` component *outside* of your router. * If you have multiple flows on the same page you will need to use a separate ``. ### The SvelteFlow component ### \ [Source on Github](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte) The `` component is the heart of your Svelte Flow application. ```svelte ``` This component takes a lot of different props, most of which are optional. We've tried to document them in groups that make sense to help you find your way. #### Common props These are the props you will most commonly use when working with Svelte Flow. * `...props: HTMLAttributes` #### Viewport props * `viewport?: Viewport` Custom viewport to be used instead of internal one * `initialViewport?: Viewport` Sets the initial position and zoom of the viewport. If a default viewport is provided but fitView is enabled, the default viewport will be ignored. * `fitView?: boolean` If set, initial viewport will show all nodes & edges * `fitViewOptions?: FitViewOptionsBase` Options to be used in combination with fitView * `minZoom?: number` Minimum zoom level * `maxZoom?: number` Maximum zoom level * `snapGrid?: SnapGrid` Grid all nodes will snap to * `onlyRenderVisibleElements?: boolean` You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport. This might improve performance when you have a large number of nodes and edges but also adds an overhead. * `translateExtent?: CoordinateExtent` By default the viewport extends infinitely. You can use this prop to set a boundary. The first pair of coordinates is the top left boundary and the second pair is the bottom right. * `preventScrolling?: boolean` Disabling this prop will allow the user to scroll the page even when their pointer is over the flow. * `attributionPosition?: PanelPosition` Set position of the attribution #### Node props * `nodeOrigin?: NodeOrigin` Defines nodes relative position to its coordinates * `nodesDraggable?: boolean` Controls if all nodes should be draggable * `nodesConnectable?: boolean` Controls if all nodes should be connectable to each other * `nodesFocusable?: boolean` When `true`, focus between nodes can be cycled with the `Tab` key and selected with the `Enter` key. This option can be overridden by individual nodes by setting their `focusable` prop. * `nodeDragThreshold?: number` With a threshold greater than zero you can control the distinction between node drag and click events. If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired. * `nodeClickDistance?: number` Distance that the mouse can move between mousedown/up that will trigger a click * `nodeExtent?: CoordinateExtent` By default the nodes can be placed anywhere. You can use this prop to set a boundary. The first pair of coordinates is the top left boundary and the second pair is the bottom right. * `elevateNodesOnSelect?: boolean` Enabling this option will raise the z-index of nodes when they are selected. #### Edge props * `edgesFocusable?: boolean` When `true`, focus between edges can be cycled with the `Tab` key and selected with the `Enter` key. This option can be overridden by individual edges by setting their `focusable` prop. * `elevateEdgesOnSelect?: boolean` Enabling this option will raise the z-index of edges when they are selected, or when the connected nodes are selected. * `defaultMarkerColor?: string | null` Color of edge markers You can pass `null` to use the CSS variable `--xy-edge-stroke` for the marker color. * `defaultEdgeOptions?: DefaultEdgeOptions` Defaults to be applied to all new edges that are added to the flow. Properties on a new edge will override these defaults if they exist. #### Event handlers ##### General Events * `oninit?: () => void` This handler gets called when the flow is finished initializing * `onflowerror?: OnError` Occasionally something may happen that causes Svelte Flow to throw an error. Instead of exploding your application, we log a message to the console and then call this event handler. You might use it for additional logging or to show a message to the user. * `ondelete?: OnDelete` This handler gets called when the user deletes nodes or edges. * `onbeforedelete?: OnBeforeDelete` This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. ##### Node Events * `onnodeclick?: NodeEventWithPointer` This event handler is called when a user clicks on a node. * `onnodedragstart?: NodeTargetEventWithPointer` This event handler is called when a user starts to drag a node. * `onnodedrag?: NodeTargetEventWithPointer` This event handler is called when a user drags a node. * `onnodedragstop?: NodeTargetEventWithPointer` This event handler is called when a user stops dragging a node. * `onnodepointerenter?: NodeEventWithPointer` This event handler is called when the pointer of a user enters a node. * `onnodepointermove?: NodeEventWithPointer` This event handler is called when the pointer of a user moves over a node. * `onnodepointerleave?: NodeEventWithPointer` This event handler is called when the pointer of a user leaves a node. * `onnodecontextmenu?: NodeEventWithPointer` This event handler is called when a user right-clicks on a node. ##### Edge Events * `onedgeclick?: ({ edge, event }: { edge: Edge; event: MouseEvent; }) => void` This event handler is called when a user clicks an edge. * `onedgecontextmenu?: ({ edge, event }: { edge: Edge; event: MouseEvent; }) => void` This event handler is called when a user right-clicks an edge. * `onedgepointerenter?: ({ edge, event }: { edge: Edge; event: PointerEvent; }) => void` This event handler is called when the pointer of a user enters an edge. * `onedgepointerleave?: ({ edge, event }: { edge: Edge; event: PointerEvent; }) => void` This event handler is called when the pointer of a user enters an edge. * `onreconnect?: OnReconnect` This event gets fired when after an edge was reconnected * `onreconnectstart?: OnReconnectStart` This event gets fired when a user starts to reconnect an edge * `onreconnectend?: OnReconnectEnd` This event gets fired when a user stops reconnecting an edge * `onbeforereconnect?: OnBeforeReconnect` This handler gets called when an edge is reconnected. You can use it to modify the edge before the update is applied. ##### Selection Events * `onselectionchanged: unknown` * `onselectionclick?: NodesEventWithPointer` This event handler is called when a user clicks the selection box. * `onselectioncontextmenu?: NodesEventWithPointer` This event handler is called when a user right-clicks the selection box. * `onselectiondragstart?: OnSelectionDrag` This event handler gets called when a user starts to drag a selection box. * `onselectiondrag?: OnSelectionDrag` This event handler gets called when a user drags a selection box. * `onselectiondragstop?: OnSelectionDrag` This event handler gets called when a user stops dragging a selection box. * `onselectionstart?: (event: PointerEvent) => void` This event handler gets called when the user starts to drag a selection box * `onselectionend?: (event: PointerEvent) => void` This event handler gets called when the user finishes dragging a selection box ##### Pane Events * `onpaneclick?: ({ event }: { event: MouseEvent; }) => void` This event handler is called when a user clicks the pane. * `onpanecontextmenu?: ({ event }: { event: MouseEvent; }) => void` This event handler is called when a user right-clicks the pane. * `onmovestart?: OnMove` This event handler is called when the user begins to pan or zoom the viewport * `onmove?: OnMove` This event handler is called when the user pans or zooms the viewport * `onmoveend?: OnMove` This event handler is called when the user stops panning or zooming the viewport ##### Connection Events * `onconnect?: OnConnect` This event gets fired when a connection successfully completes and an edge is created. * `onconnectstart?: OnConnectStart` When a user starts to drag a connection line, this event gets fired. * `onbeforeconnect?: OnBeforeConnect` This handler gets called when a new edge is created. You can use it to modify the newly created edge. * `onconnectend?: OnConnectEnd` When a user stops dragging a connection line, this event gets fired. * `isValidConnection?: IsValidConnection` * `clickConnect?: boolean` Toggles ability to make connections via clicking the handles * `onclickconnectstart?: OnConnectStart` A connection is started by clicking on a handle * `onclickconnectend?: OnConnectEnd` A connection is finished by clicking on a handle #### Connection line props * `connectionRadius?: number` The radius around a handle where you drop a connection line to create a new edge. * `connectionLineComponent?: Component<{}, {}, string>` Provide a custom snippet to be used instead of the default connection line * `connectionLineType?: ConnectionLineType` Choose from the built-in edge types to be used for connections * `connectionLineStyle?: string` Styles to be applied to the connection line * `connectionLineContainerStyle?: string` Styles to be applied to the container of the connection line #### Interaction props * `elementsSelectable?: boolean` Controls if all elements should (nodes & edges) be selectable * `autoPanOnConnect?: boolean` You can enable this prop to automatically pan the viewport while making a new connection. * `autoPanOnNodeDrag?: boolean` You can enable this prop to automatically pan the viewport while dragging a node. * `selectNodesOnDrag?: boolean` Controls if nodes should be automatically selected when being dragged * `panOnDrag?: boolean | number[]` Enabling this prop allows users to pan the viewport by clicking and dragging. You can also set this prop to an array of numbers to limit which mouse buttons can activate panning. * `selectionOnDrag?: boolean` Select multiple elements with a selection box, without pressing down selectionKey. * `selectionMode?: SelectionMode` When set to "partial", when the user creates a selection box by click and dragging nodes that are only partially in the box are still selected. * `panOnScroll?: boolean` Controls if the viewport should pan by scrolling inside the container Can be limited to a specific direction with panOnScrollMode * `panOnScrollMode?: PanOnScrollMode` This prop is used to limit the direction of panning when panOnScroll is enabled. The "free" option allows panning in any direction. * `zoomOnScroll?: boolean` Controls if the viewport should zoom by scrolling inside the container. * `zoomOnPinch?: boolean` Controls if the viewport should zoom by pinching on a touch screen * `zoomOnDoubleClick?: boolean` Controls if the viewport should zoom by double clicking somewhere on the flow * `connectionMode?: ConnectionMode` 'strict' connection mode will only allow you to connect source handles to target handles. 'loose' connection mode will allow you to connect handles of any type to one another. * `paneClickDistance?: number` Distance that the mouse can move between mousedown/up that will trigger a click * `zIndexMode?: ZIndexMode` Used to define how z-indexing is calculated for nodes and edges. 'auto' is for selections and sub flows, 'basic' for selections only, and 'manual' for no auto z-indexing. #### Keyboard props * `deleteKey?: KeyDefinition | KeyDefinition[] | null` Pressing down this key deletes all selected nodes & edges. * `selectionKey?: KeyDefinition | KeyDefinition[] | null` Pressing down this key you can select multiple elements with a selection box. * `multiSelectionKey?: KeyDefinition | KeyDefinition[] | null` Pressing down this key you can select multiple elements by clicking. * `zoomActivationKey?: KeyDefinition | KeyDefinition[] | null` If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false. By setting this prop to null you can disable this functionality. * `panActivationKey?: KeyDefinition | KeyDefinition[] | null` If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false. By setting this prop to null you can disable this functionality. * `disableKeyboardA11y?: boolean` You can use this prop to disable keyboard accessibility features such as selecting nodes or moving selected nodes with the arrow keys. #### Style props Applying certain classes to elements rendered inside the canvas will change how interactions are handled. These props let you configure those class names if you need to. * `noPanClass?: string` If an element in the canvas does not stop mouse events from propagating, clicking and dragging that element will pan the viewport. Adding the `"nopan"` class prevents this behavior and this prop allows you to change the name of that class. * `noDragClass?: string` If a node is draggable, clicking and dragging that node will move it around the canvas. Adding the `"nodrag"` class prevents this behavior and this prop allows you to change the name of that class. * `noWheelClass?: string` Typically, scrolling the mouse wheel when the mouse is over the canvas will zoom the viewport. Adding the `"nowheel"` class to an element in the canvas will prevent this behavior and this prop allows you to change the name of that class. #### Notes * The props of this component get exported as `SvelteFlowProps` ### The Background component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Background/Background.svelte) The `` component makes it convenient to render different types of backgrounds common in node-based UIs. It comes with three variants: `lines`, `dots` and `cross`. ```svelte ``` #### Props The type for props of `` component is exported as `BackgroundProps`. * `id?: string` When multiple backgrounds are present on the page, each one should have a unique id. * `bgColor?: string` Color of the background * `patternColor?: string` Color of the pattern * `patternClass?: string` Class applied to the pattern * `class?: ClassValue` Class applied to the container * `gap?: number | [number, number]` The gap between patterns. Passing in a tuple allows you to control the x and y gap independently. * `size?: number` The radius of each dot or the size of each rectangle if `BackgroundVariant.Dots` or `BackgroundVariant.Cross` is used. This defaults to 1 or 6 respectively, or ignored if `BackgroundVariant.Lines` is used. * `lineWidth?: number` The stroke thickness used when drawing the pattern. * `variant?: BackgroundVariant` Variant of the pattern. ### The BaseEdge component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/edges/BaseEdge.svelte) The `` component gets used internally for all the edges. It can be used inside a custom edge and handles the invisible helper edge and the edge label for you. ```svelte filename="CustomEdge.svelte" ``` #### Props The type for props of `` component is exported as `BaseEdgeProps`. Additionally, it extends the props of ``. * `...props: Omit, "d" | "path" | "markerStart" | "markerEnd">` #### Notes * If you want to use an edge marker with the `` component, you can pass the `markerStart` or `markerEnd` props passed to your custom edge through to the `` component. You can see all the props passed to a custom edge by looking at the [`EdgeProps`](/api-reference/types/edge-props) type. ### The ControlButton component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte) The `` component is used to render custom/ additional buttons for the `` component. ```svelte console.log('⚑️')}> ⚑️ ``` #### Props The type for props of `` component is exported as `ControlButtonProps`. Additionally, it extends the props of ` ``` #### Props * `...props: HTMLAttributes` #### Notes * By default, the toolbar is only visible when an edge is selected. You can override this behavior by setting the `isVisible` prop to `true`. ### The Handle component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/Handle/Handle.svelte) The `` component is used in your [custom nodes](/learn/customization/custom-nodes) to define connection points. ```svelte filename="CustomNode.svelte"
{data.label}
``` #### Props The type for props of `` component is exported as `HandleProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` #### Examples ##### Custom handle with validation You can create your own custom handles by wrapping the `` component. This example shows a custom handle that only allows connections when the connection source matches a given id. ```svelte ``` ##### Style handles when connecting The handle receives the additional class names `connecting` when the connection line is above the handle and `valid` if the connection is valid. You can find an example which uses these classes [here](/examples/interaction/validation). ##### Multiple handles If you need multiple source or target handles you can achieve this by creating a custom node. Normally you just use the id of a node for the `source` or `target` of an edge. If you have multiple source or target handles you need to pass an id to these handles. These ids can be used by an edge with the `sourceHandle` and `targetHandle` options, so that you can connect a specific handle. If you have a node with `id: 'node-1'` and a handle with `id: 'handle-1'` you can connect an edge to this handle by defining it with `source: 'node-1'` and `sourceHandle: 'hadnle-1'`. ##### Dynamic handles If you are programmatically changing the position or number of handles in your custom node, you need to update the node internals with the [`useUpdateNodeInternals`](/api-reference/hooks/use-update-node-internals) hook. You can find an example of how to implement a custom node with multiple handles in the [custom node guide](/learn/customization/custom-nodes) or in the [custom node example](/examples/nodes/custom-node). ##### Custom handle styles Since the handle is a div, you can use CSS to style it or pass a style prop to customize a Handle. You can see this in the [Add Node On Edge Drop](/examples/nodes/add-node-on-edge-drop) and [Simple Floating Edges](/examples/edges/simple-floating-edges) examples. ### Components ### The MiniMap component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte) The `` component can be used to render an overview of your flow. It renders each node as an SVG element and visualizes where the current viewport is in relation to the rest of the flow. ```svelte ``` #### Props The type for props of `` component is exported as `MiniMapProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` #### Examples ##### Making the mini map interactive By default, the mini map is non-interactive. To allow users to interact with the viewport by panning or zooming the minimap, you can set either of the `zoomable` or `pannable` (or both!) props to `true`. ```svelte ``` ##### Customising mini map node color The `nodeColor`, `nodeStrokeColor`, and `nodeClassName` props can be a function that takes a [`Node`](/api-reference/types/node) and computes a value for the prop. This can be used to customize the appearance of each mini map node. This example shows how to color each mini map node based on the node's type: ```svelte ``` ### The NodeResizeControl component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/487b13c9ad8903789f56c6fcfd8222f9cb74b812/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte) To create your own resizing UI, you can use the `NodeResizeControl` component where you can pass children (such as icons). #### Props The type for props of `` component is exported as `NodeResizeControlProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` ### The NodeResizer component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/487b13c9ad8903789f56c6fcfd8222f9cb74b812/packages/svelte/src/lib/plugins/NodeResizer/NodeResizer.svelte) The `` component can be used to add a resize functionality to your nodes. It renders draggable controls around the node to resize in all directions. ```svelte filename="CustomNode.svelte"
{data.label}
``` #### Props The type for props of `` component is exported as `NodeResizerProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` #### Examples Head over to the [example page](/examples/nodes/node-resizer) to see how this is done. Example: examples/nodes/node-resizer ##### App.svelte ```svelte ``` ##### CustomResizerNode.svelte ```svelte
{data.label}
``` ##### ResizableNode.svelte ```svelte
{data.label}
``` ##### ResizableNodeSelected.svelte ```svelte
{data.label}
``` ##### xy-theme.css ```css /* xyflow theme files. Delete these to start from our base */ .svelte-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #ff4000; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://svelteflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-resize-background-color-default: #9e86ed; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .svelte-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: 0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3), 0px 1px 2px 0px rgba(0, 0, 0, 0.35); --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .svelte-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .svelte-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .svelte-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .svelte-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node.selectable:hover, .svelte-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .svelte-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .svelte-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .svelte-flow.dark .svelte-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .svelte-flow__edge.selectable:hover .svelte-flow__edge-path, .svelte-flow__edge.selectable.selected .svelte-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .svelte-flow__handle { background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .svelte-flow__handle.connectionindicator:focus, .svelte-flow__handle.connectingfrom, .svelte-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .svelte-flow__node:has(.svelte-flow__resize-control.line) { border-radius: 0; } .svelte-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: var(--xy-background-color); color: var(--xy-edge-label-color-default); } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff4000; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } /* Custom Button Styling */ .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } .xy-theme__button { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### Custom Resize Controls To build custom resize controls, you can use the [NodeResizeControl](/api-reference/components/node-resize-control) component and customize it. #### Notes * Take a look at the docs for the [`NodeProps`](/api-reference/types/node-props) type or the guide on [custom nodes](/learn/customization/custom-nodes) to see how to implement your own nodes. ### The NodeToolbar component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte) The NodeToolbar component can be used to display a toolbar on a side of a node or display a tooltip for example. ```svelte filename="CustomNode.svelte"
{data.label}
``` #### Props The type for props of `` component is exported as `NodeToolbarProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` #### Notes * By default, the toolbar is only visible when a node is selected. If multiple nodes are selected it will not be visible to prevent overlapping toolbars or clutter. You can override this behavior by setting the `isVisible` prop to `true`. ### The Panel component ### \ [Source on GitHub](hthttps://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/Panel/Panel.svelte) The `` component helps you position content above the viewport. It is used internally by the [``](/api-reference/components/mini-map) and [``](/api-reference/components/controls) components. ```svelte top-left top-center top-right bottom-left bottom-center bottom-right center-left center-right ``` #### Props The type for props of `` component is exported as `PanelProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` ### The ViewportPortal component ### \ [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/react/src/components/ViewportPortal/index.tsx) `` component can be used to add components to the same viewport of the flow where nodes and edges are rendered. This is useful when you want to render your own components that are adhere to the same coordinate system as the nodes & edges and are also affected by zooming and panning ```svelte
This div is positioned at [100, 100] on the flow.
``` You can also define if you want to render the component below or above the nodes and edges by using the `target` prop. ```svelte
This div is positioned at [100, 100] on the flow.
``` #### Props The type for props of `` component is exported as `ViewportPortalProps`. Additionally, it extends the props of `
`. * `...props: HTMLAttributes` ### Hooks ### useConnection() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useConnection.svelte.ts) The `useConnection` hook returns the current connection when there is an active connection interaction. If no connection interaction is active, it returns `null` for every property. A typical use case for this hook is to colorize handles based on a certain condition (e.g. if the connection is valid or not). ```svelte ``` #### Signature Hook for receiving the current connection. This function does not accept any parameters. ###### Returns * `current: ConnectionState` ### useEdges() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts#L35) The `useEdges` hook returns an array of the current edges. ```svelte ``` #### Signature Hook for getting the current edges from the store. This function does not accept any parameters. ###### Returns * `current: Edge[]` * `update: (updateFn: (edges: Edge[]) => Edge[]) => void` * `set: (edges: Edge[]) => void` ### useInternalNode() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useInternalNode.svelte.ts) The `useInternalNode` hook returns an internal node. An internal node is used for advanced use cases like custom edges or layouting. ```svelte ``` #### Signature Hook to get an internal node by id. ###### Parameters * `id: string` the node id ###### Returns * `current: InternalNode | undefined` ### useNodeConnections() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodeConnections.svelte.ts) This hook returns an array of connections on a specific node, handle type ('source', 'target') or handle ID. ```svelte
There are currently {connections.length} incoming connections!
``` #### Signature Hook to retrieve all edges connected to a node. Can be filtered by handle type and id. ###### Parameters * `[0]?.id?: string` * `[0]?.handleType?: 'source' | 'target'` * `[0]?.handleId?: string` * `[0]?.onConnect?: (connections: HandleConnection[]) => void` * `[0]?.onDisconnect?: (connections: HandleConnection[]) => void` ###### Returns * `current: NodeConnection[]` ### useNodesData() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodesData.svelte.ts) With this hook you can receive the data of the passed node ids. ```svelte ``` #### Signature Hook for receiving data of one or multiple nodes ###### Parameters * `nodeId: string` The id (or ids) of the node to get the data from ###### Returns * `current: DistributivePick | null` #### Notes * Check the [Computing Flows example](/examples/interaction/computing-flows) to see how this hook can be used ### useNodesInitialized() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useInitialized.svelte.ts) This hook can be used to check if all nodes are initialized. It returns a signal with a boolean. ```svelte ``` #### Signature Hook for seeing if nodes are initialized This function does not accept any parameters. ###### Returns * `current: boolean` ### useNodes() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts) This hook returns the current nodes array. When you subscribe, it will trigger whenever the nodes array changes. This happens when nodes are added, removed, or updated (dragged for example). ```svelte ``` #### Signature Hook for getting the current nodes from the store. This function does not accept any parameters. ###### Returns * `current: Node[]` * `update: (updateFn: (nodes: Node[]) => Node[]) => void` * `set: (nodes: Node[]) => void` ### useOnSelectionChange() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useOnSelectionChange.svelte.ts) This hook lets you listen for changes to both node and edge selection. As the name implies, the callback you provide will be called whenever the selection of *either* nodes or edges changes. ```svelte filename="Component.svelte"

Selected nodes: {selectedNodes.join(', ')}

Selected edges: {selectedEdges.join(', ')}

``` #### Signature ###### Parameters * `onselectionchange: OnSelectionChange` ###### Returns `void` #### Notes * This hook can only be used in a component that is a child of a [``](/api-reference/svelte-flow-provider) or a [``](/api-reference/svelte-flow) component. ### useStore() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/store/index.ts/) This hook can be used to access the internal store of the Svelte Flow. This hook is only needed for advanced use cases. It should only be used if there is no other way to access the internal state. For many of the common use cases, there are dedicated hooks available such as [`useConnection`](/api-reference/hooks/use-connection), [`useNodes`](/api-reference/hooks/use-nodes), etc. ```svelte ``` #### Signature This function does not accept any parameters. ###### Returns `SvelteFlowStore` ### useSvelteFlow() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useSvelteFlow.svelte.ts) This hook returns functions to update the viewport, transform positions or get node intersections for example. ```svelte ``` #### Signature Hook for accessing the SvelteFlow instance. This function does not accept any parameters. ###### Returns * `zoomIn: (options?: { duration?: number; ease?: (t: number) => number; interpolate?: "smooth" | "linear"; }) => Promise` Zooms viewport in by 1.2. * `zoomOut: (options?: { duration?: number; ease?: (t: number) => number; interpolate?: "smooth" | "linear"; }) => Promise` Zooms viewport out by 1 / 1.2. * `getInternalNode: (id: string) => InternalNode | undefined` Returns an internal node by id. * `getNode: (id: string) => NodeType | undefined` Returns a node by id. * `getNodes: (ids?: string[] | undefined) => NodeType[]` Returns nodes. * `getEdge: (id: string) => EdgeType | undefined` Returns an edge by id. * `getEdges: (ids?: string[] | undefined) => EdgeType[]` Returns edges. * `setZoom: (zoomLevel: number, options?: ViewportHelperFunctionOptions | undefined) => Promise` Sets the current zoom level. * `getZoom: () => number` Returns the current zoom level. * `setCenter: (x: number, y: number, options?: SetCenterOptions | undefined) => Promise` Sets the center of the view to the given position. * `setViewport: (viewport: Viewport, options?: ViewportHelperFunctionOptions | undefined) => Promise` Sets the current viewport. * `getViewport: () => Viewport` Returns the current viewport. * `fitView: (options?: FitViewOptions | undefined) => Promise` Fits the view. * `getIntersectingNodes: (nodeOrRect: NodeType | { id: NodeType["id"]; } | Rect, partially?: boolean | undefined, nodesToIntersect?: NodeType[] | undefined) => NodeType[]` Returns all nodes that intersect with the given node or rect. * `isNodeIntersecting: (nodeOrRect: NodeType | Rect | { id: NodeType["id"]; }, area: Rect, partially?: boolean | undefined) => boolean` Checks if the given node or rect intersects with the passed rect. * `fitBounds: (bounds: Rect, options?: FitBoundsOptions | undefined) => Promise` Fits the view to the given bounds . * `deleteElements: ({ nodes, edges }: { nodes?: (Partial & { id: string; })[]; edges?: (Partial & { id: string; })[]; }) => Promise<{ deletedNodes: NodeType[]; deletedEdges: EdgeType[]; }>` Deletes nodes and edges. * `screenToFlowPosition: (clientPosition: XYPosition, options?: { snapToGrid: boolean; } | undefined) => XYPosition` Converts a screen / client position to a flow position. * `flowToScreenPosition: (flowPosition: XYPosition) => XYPosition` Converts a flow position to a screen / client position. * `updateNode: (id: string, nodeUpdate: Partial | ((node: NodeType) => Partial), options?: { replace: boolean; } | undefined) => void` Updates a node. * `updateNodeData: (id: string, dataUpdate: Partial | ((node: NodeType) => Partial), options?: { replace: boolean; } | undefined) => void` Updates the data attribute of a node. * `updateEdge: (id: string, edgeUpdate: Partial | ((edge: EdgeType) => Partial), options?: { replace: boolean; } | undefined) => void` Updates an edge. * `toObject: () => { nodes: NodeType[]; edges: EdgeType[]; viewport: Viewport; }` * `getNodesBounds: (nodes: (string | NodeType | InternalNode)[]) => Rect` Returns the bounds of the given nodes or node ids. * `getHandleConnections: ({ type, id, nodeId }: { type: HandleType; nodeId: string; id?: string | null; }) => HandleConnection[]` Gets all connections for a given handle belonging to a specific node. ### useUpdateNodeInternals() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useUpdateNodeInternals.svelte.ts/#L6) When you programmatically add or remove handles to a node or update a node's handle position, you need to inform Svelte Flow about it by using this hook. This will update the internal dimensions of the node and properly reposition handles on the canvas if necessary. ```svelte ``` #### Signature When you programmatically add or remove handles to a node or update a node's handle position, you need to let Svelte Flow know about it using this hook. This will update the internal dimensions of the node and properly reposition handles on the canvas if necessary. This function does not accept any parameters. ###### Returns `(nodeId?: string | string[] | undefined) => void` ### Align [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/nodes.ts#L174) The `Align` type contains the values expected by the `align` prop of the [NodeToolbar](/api-reference/components/node-toolbar) component ```ts export type Align = 'center' | 'start' | 'end'; ``` ### AriaLabelConfig [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/constants.ts/) With the `AriaLabelConfig` you can customize the aria labels used by Svelte Flow. This is useful if you want to translate the labels or if you want to change them to better suit your application. #### Fields * `node.a11yDescription.default: string` * `node.a11yDescription.keyboardDisabled: string` * `node.a11yDescription.ariaLiveMessage: ({ direction, x, y }: { direction: string; x: number; y: number; }) => string` * `edge.a11yDescription.default: string` * `controls.ariaLabel: string` * `controls.zoomIn.ariaLabel: string` * `controls.zoomOut.ariaLabel: string` * `controls.fitView.ariaLabel: string` * `controls.interactive.ariaLabel: string` * `minimap.ariaLabel: string` * `handle.ariaLabel: string` #### Default config ```js const defaultAriaLabelConfig = { 'node.a11yDescription.default': 'Press enter or space to select a node. Press delete to remove it and escape to cancel.', 'node.a11yDescription.keyboardDisabled': 'Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.', 'node.a11yDescription.ariaLiveMessage': ({ direction, x, y }: { direction: string; x: number; y: number }) => `Moved selected node ${direction}. New position, x: ${x}, y: ${y}`, 'edge.a11yDescription.default': 'Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.', // Control elements 'controls.ariaLabel': 'Control Panel', 'controls.zoomIn.ariaLabel': 'Zoom In', 'controls.zoomOut.ariaLabel': 'Zoom Out', 'controls.fitView.ariaLabel': 'Fit View', 'controls.interactive.ariaLabel': 'Toggle Interactivity', // Mini map 'minimap.ariaLabel': 'Mini Map', // Handle 'handle.ariaLabel': 'Handle', }; ``` ### BackgroundVariant The three variants are exported as an enum for convenience. You can either import the enum and use it like `BackgroundVariant.Lines` or you can use the raw string value directly. ```ts export enum BackgroundVariant { Lines = 'lines', Dots = 'dots', Cross = 'cross', } ``` ### ColorMode The color mode type specifies if the current color mode is light, dark or uses system. ```ts export type ColorMode = 'light' | 'dark' | 'system'; ``` ### ConnectionLineType With the `connectionLineType` prop on your [``](/api-reference/svelte-flow#connection-connectionLineType) component, you can configure the type of the connection line. Svelte Flow comes with built-in support for the following types: 'default' (bezier), 'straight', 'step', 'smoothstep' and 'simplebezier'. ```ts export enum ConnectionLineType { Bezier = 'default', Straight = 'straight', Step = 'step', SmoothStep = 'smoothstep', SimpleBezier = 'simplebezier', } ``` ### ConnectionMode [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L68) The `ConnectionMode` enum defines the available strategies for connecting nodes. Use `Strict` to only allow connections to valid handles, or `Loose` to allow more flexible connections. ```ts enum ConnectionMode { Strict = 'strict', Loose = 'loose', } ``` ### Connection The `Connection` type is the basic minimal description of an [`Edge`](/api-reference/types/edge) between two nodes. #### Fields The `Connection` type is the basic minimal description of an [`Edge`](/api-reference/types/edge) between two nodes. The [`addEdge`](/api-reference/utils/add-edge) util can be used to upgrade a `Connection` to an [`Edge`](/api-reference/types/edge). * `source: string` The id of the node this connection originates from. * `target: string` The id of the node this connection terminates at. * `sourceHandle: string | null` When not `null`, the id of the handle on the source node that this connection originates from. * `targetHandle: string | null` When not `null`, the id of the handle on the target node that this connection terminates at. ### CoordinateExtent A coordinate extent represents two points in a coordinate system: one in the top left corner and one in the bottom right corner. It is used to represent the bounds of nodes in the flow or the bounds of the viewport. ```ts export type CoordinateExtent = [[number, number], [number, number]]; ``` #### Notes * Props (like nodeExtent or translateExtent) that expect a `CoordinateExtent` usually default to `[[-∞, -∞], [+∞, +∞]]` to represent an unbounded extent. ### DefaultEdgeOptions Many properties on an [`Edge`](/api-reference/types/edge) are optional. When a new edge is created, the properties that are not provided will be filled in with the default values passed to the `defaultEdgeOptions` prop of the [``](/api-reference/svelte-flow#defaultedgeoptions) component. #### Fields * `type?: string | undefined` Type of edge defined in `edgeTypes`. * `animated?: boolean` * `hidden?: boolean` * `deletable?: boolean` * `selectable?: boolean` * `data?: Record` Arbitrary data passed to an edge. * `markerStart?: EdgeMarkerType` Set the marker on the beginning of an edge. * `markerEnd?: EdgeMarkerType` Set the marker on the end of an edge. * `zIndex?: number` * `ariaLabel?: string` * `interactionWidth?: number` ReactFlow renders an invisible path around each edge to make them easier to click or tap on. This property sets the width of that invisible path. * `label?: string` * `labelStyle?: string` * `style?: string` * `class?: ClassValue` * `focusable?: boolean` * `ariaRole?: AriaRole | null | undefined` The ARIA role attribute for the edge, used for accessibility. * `domAttributes?: Omit, "id" | "style" | "class" | "role" | "aria-label" | "dangerouslySetInnerHTML">` General escape hatch for adding custom attributes to the edge's DOM element. ### EdgeMarker You can customize the built-in edge markers with the `edgeMarker` [Edge](/api-reference/types/edge) prop. #### Fields Edges can optionally have markers at the start and end of an edge. The `EdgeMarker` type is used to configure those markers! Check the docs for [`MarkerType`](/api-reference/types/marker-type) for details on what types of edge marker are available. * `type: MarkerType | "arrow" | "arrowclosed"` * `color?: string | null` * `width?: number` * `height?: number` * `markerUnits?: string` * `orient?: string` * `strokeWidth?: number` ### EdgeProps When you implement a custom edge it is wrapped in a component that enables some basic functionality. Your custom edge component receives the following props: #### Fields Custom edge component props. * `id: EdgeType["id"]` Unique id of an edge. * `type: EdgeType["type"] & string` Type of edge defined in `edgeTypes`. * `source: EdgeType["source"]` Id of source node. * `target: EdgeType["target"]` Id of target node. * `animated?: EdgeType["animated"]` * `hidden?: EdgeType["hidden"]` * `deletable?: EdgeType["deletable"]` * `selectable?: EdgeType["selectable"]` * `data?: EdgeType["data"]` Arbitrary data passed to an edge. * `selected?: EdgeType["selected"]` * `markerStart?: EdgeType["markerStart"] & string` Set the marker on the beginning of an edge. * `markerEnd?: EdgeType["markerEnd"] & string` Set the marker on the end of an edge. * `zIndex?: EdgeType["zIndex"]` * `ariaLabel?: EdgeType["ariaLabel"]` * `interactionWidth?: EdgeType["interactionWidth"]` ReactFlow renders an invisible path around each edge to make them easier to click or tap on. This property sets the width of that invisible path. * `label?: EdgeType["label"]` * `labelStyle?: EdgeType["labelStyle"]` * `style?: EdgeType["style"]` * `class?: EdgeType["class"]` * `focusable?: EdgeType["focusable"]` * `ariaRole?: EdgeType["ariaRole"]` The ARIA role attribute for the edge, used for accessibility. * `domAttributes?: EdgeType["domAttributes"]` General escape hatch for adding custom attributes to the edge's DOM element. * `sourceX: number` * `sourceY: number` * `targetX: number` * `targetY: number` * `sourcePosition: Position` * `targetPosition: Position` * `sourceHandleId?: string | null` * `targetHandleId?: string | null` ### EdgeTypes [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/edges.ts#L140) The `EdgeTypes` type is a record that maps edge type identifiers to their corresponding Svelte components. This allows you to define custom edge types and their implementations. ```ts type EdgeTypes = Record; ``` ### Edge ### Edge\ An `Edge` is the complete description with everything Svelte Flow needs to know in order to render it. #### Fields An `Edge` is the complete description with everything Svelte Flow needs to know in order to render it. * `id: string` Unique id of an edge. * `type?: EdgeType` Type of edge defined in `edgeTypes`. * `source: string` Id of source node. * `target: string` Id of target node. * `sourceHandle?: string | null` Id of source handle, only needed if there are multiple handles per node. * `targetHandle?: string | null` Id of target handle, only needed if there are multiple handles per node. * `animated?: boolean` * `hidden?: boolean` * `deletable?: boolean` * `selectable?: boolean` * `data?: EdgeData` Arbitrary data passed to an edge. * `selected?: boolean` * `markerStart?: EdgeMarkerType` Set the marker on the beginning of an edge. * `markerEnd?: EdgeMarkerType` Set the marker on the end of an edge. * `zIndex?: number` * `ariaLabel?: string` * `interactionWidth?: number` ReactFlow renders an invisible path around each edge to make them easier to click or tap on. This property sets the width of that invisible path. * `label?: string` * `labelStyle?: string` * `style?: string` * `class?: ClassValue` * `focusable?: boolean` * `ariaRole?: AriaRole | null | undefined` The ARIA role attribute for the edge, used for accessibility. * `domAttributes?: Omit, "role" | "id" | "style" | "class" | "aria-label" | "dangerouslySetInnerHTML">` General escape hatch for adding custom attributes to the edge's DOM element. ### FitViewOptions When calling `fitView` these options can be used to customize the behavior. For example, the `duration` option can be used to transform the viewport smoothly over a given amount of time. #### Fields * `padding?: Padding` * `includeHiddenNodes?: boolean` * `minZoom?: number` * `maxZoom?: number` * `duration?: number` * `ease?: (t: number) => number` * `interpolate?: "smooth" | "linear"` * `nodes?: (NodeType | { id: string; })[]` ### Types ### InternalNode ### InternalNode\ The internal node is an extension of the user node. It is used internally and has some more information that is not exposed to the user directly, like `positionAbsolute` and `handleBounds`. #### Fields The node data structure that gets used for internal nodes. There are some data structures added under node.internal that are needed for tracking some properties * `width?: NodeType["width"]` * `height?: NodeType["height"]` * `id: NodeType["id"]` Unique id of a node. * `position: NodeType["position"]` Position of a node on the pane. * `type?: NodeType["type"]` Type of node defined in nodeTypes * `data: NodeType["data"]` Arbitrary data passed to a node. * `sourcePosition?: NodeType["sourcePosition"]` Only relevant for default, source, target nodeType. Controls source position. * `targetPosition?: NodeType["targetPosition"]` Only relevant for default, source, target nodeType. Controls target position. * `hidden?: NodeType["hidden"]` Whether or not the node should be visible on the canvas. * `selected?: NodeType["selected"]` * `dragging?: NodeType["dragging"]` Whether or not the node is currently being dragged. * `draggable?: NodeType["draggable"]` Whether or not the node is able to be dragged. * `selectable?: NodeType["selectable"]` * `connectable?: NodeType["connectable"]` * `deletable?: NodeType["deletable"]` * `dragHandle?: NodeType["dragHandle"]` A class name that can be applied to elements inside the node that allows those elements to act as drag handles, letting the user drag the node by clicking and dragging on those elements. * `initialWidth?: NodeType["initialWidth"]` * `initialHeight?: NodeType["initialHeight"]` * `parentId?: NodeType["parentId"]` Parent node id, used for creating sub-flows. * `zIndex?: NodeType["zIndex"]` * `extent?: NodeType["extent"]` Boundary a node can be moved in. * `expandParent?: NodeType["expandParent"]` When `true`, the parent node will automatically expand if this node is dragged to the edge of the parent node's bounds. * `ariaLabel?: NodeType["ariaLabel"]` * `origin?: NodeType["origin"]` Origin of the node relative to its position. * `handles?: NodeType["handles"]` * `class?: NodeType["class"]` * `style?: NodeType["style"]` * `focusable?: NodeType["focusable"]` * `ariaRole?: NodeType["ariaRole"]` The ARIA role attribute for the node element, used for accessibility. * `domAttributes?: NodeType["domAttributes"]` General escape hatch for adding custom attributes to the node's DOM element. * `measured: { width?: number; height?: number; }` * `internals: { positionAbsolute: XYPosition; z: number; rootParentIndex?: number; userNode: NodeType; handleBounds?: NodeHandleBounds; bounds?: NodeBounds; }` #### Notes * The internal node can be accessed using the [`useInternalNode`](/api-reference/hooks/use-internal-node) hook or [`getInternalNode`](/api-reference/hooks/use-svelte-flow#get-internal-node). ### IsValidConnection [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L43) The `IsValidConnection` type is a function that determines whether a connection between nodes is valid. It receives an edge or connection object and returns a boolean indicating whether the connection is valid. ```ts type IsValidConnection = (edge: Edge | Connection) => boolean; ``` ###### Parameters * `edge: EdgeBase | Connection` ###### Returns `boolean` ### KeyDefinition A key definition is a string or an object that describes a key and a modifier. It is used for defining built-in keybindings like `selectionKey` or `deleteKey`. ```ts export type KeyDefinitionObject = { key: string; modifier?: KeyModifier }; export type KeyDefinition = string | KeyDefinitionObject; ``` ### MarkerType Svelte Flow comes with two built-in markers: `MarkerType.Arrow` and `MarkerType.ArrowClosed`. You can use these by setting the `markerStart`/ `markerEnd` [Edge](/api-reference/types/edge) option. ```ts export enum MarkerType { Arrow = 'arrow', ArrowClosed = 'arrowclosed', } ``` ### NodeConnection [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts/#L36-L37) The `NodeConnection` type is an extension of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`. #### Fields The `NodeConnection` type is an extension of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`. * `source: string` The id of the node this connection originates from. * `target: string` The id of the node this connection terminates at. * `sourceHandle: string | null` When not `null`, the id of the handle on the source node that this connection originates from. * `targetHandle: string | null` When not `null`, the id of the handle on the target node that this connection terminates at. * `edgeId: string` ### NodeEventWithPointer [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/events.ts#4) The `NodeEventWithPointer` type represents an event that occurs during node interactions, including pointer position and event details. It extends the base node event with pointer-specific information. ```ts type NodeEventWithPointer = { event: PointerEvent; node: NodeType; }; ``` ###### Parameters * `__0: { node: NodeType; event: T; }` ###### Returns `void` ### NodeOrigin The origin of a Node determines how it is placed relative to its own coordinates. `[0, 0]` places it at the top left corner, `[0.5, 0.5]` right in the center and `[1, 1]` at the bottom right of its position. ```ts type NodeOrigin = [number, number]; ``` ### NodeProps When you implement a [custom node](/learn/customization/custom-nodes) it is wrapped in a component that enables basic functionality like drag, select and remove. A custom node gets the following props: * `id: NodeType["id"]` Unique id of a node. * `data: NodeType["data"]` Arbitrary data passed to a node. * `width?: NodeType["width"]` * `height?: NodeType["height"]` * `sourcePosition?: NodeType["sourcePosition"]` Only relevant for default, source, target nodeType. Controls source position. * `targetPosition?: NodeType["targetPosition"]` Only relevant for default, source, target nodeType. Controls target position. * `dragHandle?: NodeType["dragHandle"]` A class name that can be applied to elements inside the node that allows those elements to act as drag handles, letting the user drag the node by clicking and dragging on those elements. * `parentId?: NodeType["parentId"]` Parent node id, used for creating sub-flows. * `type: any` Type of node defined in nodeTypes * `dragging: NodeType["dragging"]` Whether or not the node is currently being dragged. * `zIndex: NodeType["zIndex"]` * `selectable: NodeType["selectable"]` * `deletable: NodeType["deletable"]` * `selected: NodeType["selected"]` * `draggable: NodeType["draggable"]` Whether or not the node is able to be dragged. * `isConnectable: boolean` Whether a node is connectable or not. * `positionAbsoluteX: number` Position absolute x value. * `positionAbsoluteY: number` Position absolute y value. #### Notes * If you have controls (like a slider or an input) or other elements inside your custom node that **should not drag the node** you can add the class `nodrag` to those elements. This prevents the default drag behavior as well as the default node selection behavior when elements with this class are clicked. ```svelte filename="CustomNode.svelte"
``` * If you have scroll containers inside your custom node you can add the class `nowheel` to **disable the default canvas pan behavior when scrolling** inside your custom nodes. ```svelte filename="CustomNode.svelte"

Scrollable content...

``` * When creating your own custom nodes, you will also need to remember to style them! Custom nodes have no default styles unlike the built-in nodes so you can use any [styling method you like](/learn/customization/theming). ### NodeTargetEventWithPointer [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/events.ts#20) The `NodeTargetEventWithPointer` type represents an event that occurs during target node interactions, including pointer position and event details. It extends the base node event with pointer-specific information and target node data. ```ts type NodeTargetEventWithPointer = { event: PointerEvent; node: NodeType; targetNode: NodeType; }; ``` ###### Parameters * `__0: { targetNode: NodeType | null; nodes: NodeType[]; event: T; }` ###### Returns `void` ### NodeTypes [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/nodes.ts#L33) The `NodeTypes` type is a record that maps node type identifiers to their corresponding Svelte components. This allows you to define custom node types and their implementations. ```ts type NodeTypes = Record; ``` ### Node ### Node\ The `Node` type represents everything Svelte Flow needs to know about a given node. #### Notes * You shouldn't try to set the `measured.width` or `measured.height` of a node directly. It is measured internally by Svelte Flow and used when rendering the node in the viewport. To control a node's size you should use the `width` and `height` attributes. #### Fields The node data structure that gets used for the nodes prop. * `id: string` Unique id of a node. * `position: XYPosition` Position of a node on the pane. * `data: NodeData` Arbitrary data passed to a node. * `sourcePosition?: Position` Only relevant for default, source, target nodeType. Controls source position. * `targetPosition?: Position` Only relevant for default, source, target nodeType. Controls target position. * `hidden?: boolean` Whether or not the node should be visible on the canvas. * `selected?: boolean` * `dragging?: boolean` Whether or not the node is currently being dragged. * `draggable?: boolean` Whether or not the node is able to be dragged. * `selectable?: boolean` * `connectable?: boolean` * `deletable?: boolean` * `dragHandle?: string` A class name that can be applied to elements inside the node that allows those elements to act as drag handles, letting the user drag the node by clicking and dragging on those elements. * `width?: number` * `height?: number` * `initialWidth?: number` * `initialHeight?: number` * `parentId?: string` Parent node id, used for creating sub-flows. * `zIndex?: number` * `extent?: CoordinateExtent | "parent" | null` Boundary a node can be moved in. * `expandParent?: boolean` When `true`, the parent node will automatically expand if this node is dragged to the edge of the parent node's bounds. * `ariaLabel?: string` * `origin?: NodeOrigin` Origin of the node relative to its position. * `handles?: NodeHandle[]` * `measured?: { width?: number; height?: number; }` * `type?: string | NodeType | (NodeType & undefined)` Type of node defined in nodeTypes * `class?: ClassValue` * `style?: string` * `focusable?: boolean` * `ariaRole?: AriaRole | null | undefined` The ARIA role attribute for the node element, used for accessibility. * `domAttributes?: Omit, "id" | "draggable" | "class" | "style" | "role" | "aria-label" | "dangerouslySetInnerHTML" | keyof DOMAttributes>` General escape hatch for adding custom attributes to the node's DOM element. ### OnBeforeConnect [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/lib/types/general.ts#L31) The `OnBeforeConnect` type is a callback function that is called before a connection is created. It can prevent the connection or modify it by returning `false` or a modified connection. ```ts type OnBeforeConnect = (connection: Connection) => Promise; ``` ###### Parameters * `connection: Connection` ###### Returns `false | void | EdgeType | Connection | null` ### OnBeforeDelete [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L38) The `OnBeforeDelete` type is a callback function that is called before nodes or edges are deleted. It can prevent deletion or modify the items to be deleted by returning `false` or a modified set of nodes and edges. ```ts type OnBeforeDelete< NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase, > = ({ nodes, edges, }: { nodes: NodeType[]; edges: EdgeType[]; }) => Promise; ``` ###### Parameters * `__0: { nodes: NodeType[]; edges: EdgeType[]; }` ###### Returns `Promise` ### OnBeforeReconnect [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L34) The `OnBeforeReconnect` type is a callback function that is called before an edge is reconnected. The callback receives the new edge (with the updated connection) and the original edge being reconnected. You can return the new edge or a modified version of it. Returning `false`, `null`, or `undefined` aborts the reconnection. Aborting the reconnection restores the edge to its original state. To delete the edge instead, call [deleteElements](/api-reference/hooks/use-svelte-flow#deleteelements) to remove it, then return `null`. See the [Reconnect Edge](/examples/edges/reconnect-edge) example for more information on how to implement edge reconnection. ```ts type OnBeforeReconnect = ( newEdge: EdgeType, oldEdge: EdgeType, ) => EdgeType | void | false | null; ``` ###### Parameters * `newEdge: EdgeType` * `oldEdge: EdgeType` ###### Returns `false | void | EdgeType | null` ### OnConnectEnd [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L89) The `OnConnectEnd` type represents a callback function that is called when finishing or canceling a connection attempt. It receives the mouse or touch event and the final state of the connection attempt. ```ts type OnConnectEnd = ( event: MouseEvent | TouchEvent, connectionState: FinalConnectionState, ) => void; ``` ###### Parameters * `event: MouseEvent | TouchEvent` * `connectionState: FinalConnectionState` ###### Returns `void` ### OnConnectStart [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L79) The `OnConnectStart` type is a callback function that is called when starting to create a connection between nodes. It receives the mouse or touch event and parameters about the connection being started. ```ts type OnConnectStart = ( event: MouseEvent | TouchEvent, params: OnConnectStartParams, ) => void; ``` ###### Parameters * `event: MouseEvent | TouchEvent` * `params: OnConnectStartParams` ###### Returns `void` ### OnConnect [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L80) The `OnConnect` type is a callback function that is called when a new connection is created between nodes. It receives a connection object containing the source and target node IDs and their respective handle IDs. ```ts type OnConnect = (connection: Connection) => void; ``` ###### Parameters * `connection: Connection` ###### Returns `void` ### OnDelete [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts) The `OnDelete` type is a callback function that is called when nodes or edges are deleted from the flow. It receives the deleted nodes and edges. ```ts type OnDelete< NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase, > = ({ nodes, edges }: { nodes: NodeType[]; edges: EdgeType[] }) => void; ``` This type can be used to type the `onDelete` function with a custom node and edge type. ###### Parameters * `params: { nodes: NodeType[]; edges: EdgeType[]; }` ###### Returns `void` ### OnError [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L88) The `OnError` type is a callback function that is called when an error occurs in SvelteFlow. It receives the error ID and a message describing the error. ```ts type OnError = (id: string, message: string) => void; ``` ###### Parameters * `id: string` * `message: string` ###### Returns `void` ### OnMove [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L86) The `OnMove` type is a callback that fires whenever the viewport is moved, either by user interaction or programmatically. It receives the triggering event and the new viewport state. ```ts type OnMove = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void; ``` This type is used to define the `onMove` handler. ###### Parameters * `event: MouseEvent | TouchEvent` * `viewport: Viewport` ###### Returns `void` ### OnReconnectEnd [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L89) The `OnReconnectEnd` type is a callback that fires when the reconnection process for an edge is completed or canceled. It receives the triggering event, the edge, the handle type, and the final connection state. ```ts type OnReconnectEnd = ( event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType, connectionState: FinalConnectionState, ) => void; ``` ###### Parameters * `event: MouseEvent | TouchEvent` * `edge: EdgeType` * `handleType: 'source' | 'target'` * `connectionState: FinalConnectionState` ###### Returns `void` ### OnReconnectStart [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L84) The `OnReconnectStart` type is a callback function that is called when reconnecting an existing edge. It receives the mouse or touch event, the edge being reconnected, and the type of handle being used. ```ts type OnReconnectStart = ( event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType, ) => void; ``` ###### Parameters * `event: MouseEvent | TouchEvent` * `edge: EdgeType` * `handleType: 'source' | 'target'` ###### Returns `void` ### OnReconnect [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L83) The `OnReconnect` type is a callback function that is called when an existing edge is reconnected to a different node or handle. It receives the old edge and the new connection details. ```ts type OnReconnect = ( oldEdge: EdgeType, newConnection: Connection, ) => void; ``` ###### Parameters * `oldEdge: EdgeType` * `newConnection: Connection` ###### Returns `void` ### OnSelectionDrag [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/events.ts#L74) The `OnSelectionDrag` type is a callback function that is called when dragging a selection of nodes. It receives the mouse event and an array of the nodes being dragged. ```ts type OnSelectionDrag = ( event: MouseEvent, nodes: NodeType[], ) => void; ``` ###### Parameters * `event: MouseEvent` * `nodes: NodeBase, string | undefined>[]` ###### Returns `void` ### PanOnScrollMode [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#167) The `PanOnScrollMode` enum controls the panning behavior of the viewport when the user scrolls. Choose `Free` for unrestricted panning, `Vertical` for up-and-down only, or `Horizontal` for left-and-right only. ```ts enum PanOnScrollMode { Free = 'free', Vertical = 'vertical', Horizontal = 'horizontal', } ``` ### PanelPosition This type is mostly used to help position things on top of the flow viewport. For example both the [``](/api-reference/components/mini-map) and [``](/api-reference/components/controls) components take a `position` prop of this type. ```ts export type PanelPosition = | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'center-left' | 'center-right'; ``` #### Fields ### Position While [`PanelPosition`](/api-reference/types/panel-position) can be used to place a component in the corners of a container, the `Position` enum is less precise and used primarily in relation to edges and handles. ```ts export enum Position { Left = 'left', Top = 'top', Right = 'right', Bottom = 'bottom', } ``` ### Rect [Source on GitHub](https://github.com/xyflow/xyflow/blob/f0ce2c876d8688e13632bc86286cf857f86dead6/packages/system/src/types/utils.ts/#L39-L40) The `Rect` type defines a rectangle in a two-dimensional space with dimensions and a position. * `width: number` * `height: number` * `x: number` * `y: number` ### SelectionMode [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L223) The `SelectionMode` enum determines how node selection works. Use `Full` to require the entire node to be within the selection area, or `Partial` to allow overlap. ```ts enum SelectionMode { Partial = 'partial', Full = 'full', } ``` ### SnapGrid The SnapGrid type is used to define the grid size for snapping nodes on the pane. ```ts type SnapGrid = [number, number]; ``` ### SvelteFlowStore The `SvelteFlowStore` type is the structure of the internal Svelte Flow Store, that you can access via the [useStore](/api-reference/hooks/use-store) hook. The internal Svelte Flow store should only be used for advanced use cases. It's not recommended to be used directly. #### Fields * `flowId: string` * `domNode: HTMLDivElement | null` * `panZoom: PanZoomInstance | null` * `width: number` * `height: number` * `zIndexMode: ZIndexMode` * `nodesInitialized: boolean` * `viewportInitialized: boolean` * `_edges: EdgeType[]` * `nodes: NodeType[]` * `edges: EdgeType[]` * `_prevSelectedNodes: NodeType[]` * `_prevSelectedNodeIds: Set` * `selectedNodes: NodeType[]` * `_prevSelectedEdges: EdgeType[]` * `_prevSelectedEdgeIds: Set` * `selectedEdges: EdgeType[]` * `selectionChangeHandlers: Map>` * `nodeLookup: NodeLookup>` * `parentLookup: ParentLookup>` * `connectionLookup: ConnectionLookup` * `edgeLookup: EdgeLookup` * `_prevVisibleEdges: Map>` * `visible: { nodes: Map>; edges: Map>; }` * `nodesDraggable: boolean` * `nodesConnectable: boolean` * `elementsSelectable: boolean` * `nodesFocusable: boolean` * `edgesFocusable: boolean` * `disableKeyboardA11y: boolean` * `minZoom: number` * `maxZoom: number` * `nodeOrigin: NodeOrigin` * `nodeExtent: CoordinateExtent` * `translateExtent: CoordinateExtent` * `defaultEdgeOptions: Partial` * `nodeDragThreshold: number` * `autoPanOnNodeDrag: boolean` * `autoPanOnConnect: boolean` * `autoPanOnNodeFocus: boolean` * `autoPanSpeed: number` * `connectionDragThreshold: number` * `fitViewQueued: boolean` * `fitViewOptions: FitViewOptions | undefined` * `fitViewResolver: any` * `snapGrid: SnapGrid | null` * `dragging: boolean` * `selectionRect: SelectionRect | null` * `selectionKeyPressed: boolean` * `multiselectionKeyPressed: boolean` * `deleteKeyPressed: boolean` * `panActivationKeyPressed: boolean` * `zoomActivationKeyPressed: boolean` * `selectionRectMode: string | null` * `ariaLiveMessage: string` * `selectionMode: SelectionMode` * `nodeTypes: NodeTypes` * `edgeTypes: EdgeTypes` * `noPanClass: string` * `noDragClass: string` * `noWheelClass: string` * `ariaLabelConfig: { 'node.a11yDescription.default': string; 'node.a11yDescription.keyboardDisabled': string; 'node.a11yDescription.ariaLiveMessage': ({ direction, x, y }: { direction: string; x: number; y: number; }) => string; ... 7 more ...; 'handle.ariaLabel': string; }` * `_viewport: Viewport` * `viewport: Viewport` * `_connection: ConnectionState` * `connection: ConnectionState` * `connectionMode: ConnectionMode` * `connectionRadius: number` * `isValidConnection: IsValidConnection` * `selectNodesOnDrag: boolean` * `defaultMarkerColor: string | null` * `markers: MarkerProps[]` * `onlyRenderVisibleElements: boolean` * `onerror: OnError` * `ondelete?: OnDelete` * `onbeforedelete?: OnBeforeDelete` * `onbeforeconnect?: OnBeforeConnect` * `onconnect?: OnConnect` * `onconnectstart?: OnConnectStart` * `onconnectend?: OnConnectEnd` * `onbeforereconnect?: OnBeforeReconnect` * `onreconnect?: OnReconnect` * `onreconnectstart?: OnReconnectStart` * `onreconnectend?: OnReconnectEnd` * `clickConnect?: boolean` * `onclickconnectstart?: OnConnectStart` * `onclickconnectend?: OnConnectEnd` * `clickConnectStartHandle: Pick | null` * `onselectiondrag?: OnSelectionDrag` * `onselectiondragstart?: OnSelectionDrag` * `onselectiondragstop?: OnSelectionDrag` * `resolveFitView: () => Promise` * `_prefersDark: MediaQuery` * `colorMode: ColorModeClass` * `resetStoreValues: () => void` * `setNodeTypes: (nodeTypes: NodeTypes) => void` * `setEdgeTypes: (edgeTypes: EdgeTypes) => void` * `addEdge: (edge: EdgeType | Connection) => void` * `zoomIn: (options?: ViewportHelperFunctionOptions | undefined) => Promise` * `zoomOut: (options?: ViewportHelperFunctionOptions | undefined) => Promise` * `setMinZoom: (minZoom: number) => void` * `setMaxZoom: (maxZoom: number) => void` * `setTranslateExtent: (extent: CoordinateExtent) => void` * `fitView: (options?: FitViewOptions | undefined) => Promise` * `setCenter: (x: number, y: number, options?: ViewportHelperFunctionOptions & { zoom?: number; }) => Promise` * `updateNodePositions: UpdateNodePositions` * `updateNodeInternals: (updates: Map) => void` * `unselectNodesAndEdges: (params?: { nodes?: NodeType[]; edges?: EdgeType[]; } | undefined) => void` * `addSelectedNodes: (ids: string[]) => void` * `addSelectedEdges: (ids: string[]) => void` * `handleNodeSelection: (id: string, unselect?: boolean | undefined, nodeRef?: HTMLDivElement | null | undefined) => void` * `handleEdgeSelection: (id: string) => void` * `moveSelectedNodes: (direction: XYPosition, factor: number) => void` * `panBy: (delta: XYPosition) => Promise` * `updateConnection: UpdateConnection` * `cancelConnection: () => void` * `reset: () => void` ### Viewport Internally, Svelte Flow maintains a coordinate system that is independent of the rest of the page. The `Viewport` type tells you where in that system your flow is currently being display at and how zoomed in or out it is. #### Fields Internally, React Flow maintains a coordinate system that is independent of the rest of the page. The `Viewport` type tells you where in that system your flow is currently being display at and how zoomed in or out it is. * `x: number` * `y: number` * `zoom: number` #### Notes * A `Transform` has the same properties as the viewport, but they represent different things. Make sure you don't get them muddled up or things will start to look weird! ### XYPosition All positions are stored in an object with x and y coordinates. #### Fields All positions are stored in an object with x and y coordinates. * `x: number` * `y: number` ### ZIndexMode The ZIndexMode type is used to define how z-indexing is calculated for nodes and edges. * `auto` mode will automatically manage z-indexing for selections and sub flows. * `basic` mode will only manage z-indexing for selections. * `manual` mode does not apply any automatic z-indexing. ```ts export type ZIndexMode = 'auto' | 'basic' | 'manual'; ``` ### addEdge() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/general.ts/#L104-L138) This util is a convenience function to add a new [`Edge`](/api-reference/types/edge) to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. ```js import { addEdge } from '@xyflow/svelte'; let edges = $state.raw([]); const onAddEdge = () => { const newEdge = { id: '1-2', source: '1', target: '2', }; edges = addEdge(newEdge, edges.current); }; ``` #### Signature This util is a convenience function to add a new Edge to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. ###### Parameters * `edgeParams: EdgeType | Connection` Either an `Edge` or a `Connection` you want to add. * `edges: EdgeType[]` The array of all current edges. * `options.getEdgeId?: GetEdgeId` Custom function to generate edge IDs. If not provided, the default `getEdgeId` function is used. ###### Returns `EdgeType[]` #### Notes * If an edge with the same `target` and `source` already exists (and the same `targetHandle` and `sourceHandle` if those are set), then this util won't add a new edge even if the `id` property is different. ### getBezierPath() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/bezier-edge.ts/#L95) The `getBezierPath` util returns everything you need to render a bezier edge between two nodes. ```js import { Position, getBezierPath } from '@xyflow/svelte'; const source = { x: 0, y: 20 }; const target = { x: 150, y: 100 }; const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({ sourceX: source.x, sourceY: source.y, sourcePosition: Position.Right, targetX: target.x, targetY: target.y, targetPosition: Position.Left, }); console.log(path); //=> "M0,20 C75,20 75,100 150,100" console.log(labelX, labelY); //=> 75, 60 console.log(offsetX, offsetY); //=> 75, 40 ``` #### Signature The `getBezierPath` util returns everything you need to render a bezier edge between two nodes. ###### Parameters * `[0].sourceX: number` The `x` position of the source handle. * `[0].sourceY: number` The `y` position of the source handle. * `[0].sourcePosition?: Position` The position of the source handle. * `[0].targetX: number` The `x` position of the target handle. * `[0].targetY: number` The `y` position of the target handle. * `[0].targetPosition?: Position` The position of the target handle. * `[0].curvature?: number` The curvature of the bezier edge. ###### Returns `[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]` #### Notes * This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once. ### getConnectedEdges() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L327-L337) Given an array of nodes that may be connected to one another and an array of *all* your edges, this util gives you an array of edges that connect any of the given nodes together. ```js import { getConnectedEdges } from '@xyflow/svelte'; let nodes = $state.raw([]); let edges = $state.raw([]); const connectedEdges = getConnectedEdges(nodes.value, edges.value); ``` #### Signature This utility filters an array of edges, keeping only those where either the source or target node is present in the given array of nodes. ###### Parameters * `nodes: NodeType[]` Nodes you want to get the connected edges for. * `edges: EdgeType[]` All edges. ###### Returns `EdgeType[]` ### getIncomers() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L121-L137) This util is used to tell you what nodes, if any, are connected to the given node as the *source* of an edge. ```ts import { getIncomers } from '@xyflow/svelte'; let nodes = $state.raw([]); let edges = $state.raw([]); const incomers = getIncomers( { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } }, nodes.value, edges.value, ); ``` #### Signature This util is used to tell you what nodes, if any, are connected to the given node as the *source* of an edge. ###### Parameters * `node: NodeType | { id: string; }` The node to get the connected nodes from. * `nodes: NodeType[]` The array of all nodes. * `edges: EdgeType[]` The array of all edges. ###### Returns `NodeType[]` ### getNodesBounds() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L195-L229) Returns the bounding box that contains all the given nodes in an array. This can be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds) to calculate the correct transform to fit the given nodes in a viewport. This function was previously called `getRectOfNodes`, which will be removed in v12. ```js import { getNodesBounds } from '@xyflow/svelte'; let nodes = $state.raw([ { id: 'a', position: { x: 0, y: 0 }, data: { label: 'a' }, width: 50, height: 25, }, { id: 'b', position: { x: 100, y: 100 }, data: { label: 'b' }, width: 50, height: 25, }, ]); const bounds = getNodesBounds(nodes.value); ``` #### Signature Returns the bounding box that contains all the given nodes in an array. This can be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds) to calculate the correct transform to fit the given nodes in a viewport. ###### Parameters * `nodes: (string | NodeType | InternalNodeBase)[]` Nodes to calculate the bounds for. * `params.nodeOrigin?: NodeOrigin` Origin of the nodes: `[0, 0]` for top-left, `[0.5, 0.5]` for center. * `params.nodeLookup?: NodeLookup>` ###### Returns `Rect` ### getOutgoers() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L79-L80) This util is used to tell you what nodes, if any, are connected to the given node as the *target* of an edge. ```ts import { getOutgoers } from '@xyflow/svelte'; let nodes = $state.raw([]); let edges = $state.raw([]); const incomers = getOutgoers( { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } }, nodes.value, edges.value, ); ``` #### Signature This util is used to tell you what nodes, if any, are connected to the given node as the *target* of an edge. ###### Parameters * `node: NodeType | { id: string; }` The node to get the connected nodes from. * `nodes: NodeType[]` The array of all nodes. * `edges: EdgeType[]` The array of all edges. ###### Returns `NodeType[]` ### getSmoothStepPath() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/smoothstep-edge.ts/#L244-L245) The `getSmoothStepPath` util returns everything you need to render a stepped path between two nodes. The `borderRadius` property can be used to choose how rounded the corners of those steps are. ```js import { Position, getSmoothStepPath } from '@xyflow/svelte'; const source = { x: 0, y: 20 }; const target = { x: 150, y: 100 }; const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({ sourceX: source.x, sourceY: source.y, sourcePosition: Position.Right, targetX: target.x, targetY: target.y, targetPosition: Position.Left, }); console.log(path); //=> "M0 20L20 20L 70,20Q 75,20 75,25L 75,95Q ..." console.log(labelX, labelY); //=> 75, 60 console.log(offsetX, offsetY); //=> 75, 40 ``` #### Signature The `getSmoothStepPath` util returns everything you need to render a stepped path between two nodes. The `borderRadius` property can be used to choose how rounded the corners of those steps are. ###### Parameters * `[0].sourceX: number` The `x` position of the source handle. * `[0].sourceY: number` The `y` position of the source handle. * `[0].sourcePosition?: Position` The position of the source handle. * `[0].targetX: number` The `x` position of the target handle. * `[0].targetY: number` The `y` position of the target handle. * `[0].targetPosition?: Position` The position of the target handle. * `[0].borderRadius?: number` * `[0].centerX?: number` * `[0].centerY?: number` * `[0].offset?: number` * `[0].stepPosition?: number` Controls where the bend occurs along the path. 0 = at source, 1 = at target, 0.5 = midpoint ###### Returns `[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]` #### Notes * This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once. * You can set the `borderRadius` property to `0` to get a step edge path. ### getStraightPath() [Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/straight-edge.ts/#L43-L44) Calculates the straight line path between two points. ```js import { getStraightPath } from '@xyflow/svelte'; const source = { x: 0, y: 20 }; const target = { x: 150, y: 100 }; const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({ sourceX: source.x, sourceY: source.y, targetX: target.x, targetY: target.y, }); console.log(path); //=> "M 0,20L 150,100" console.log(labelX, labelY); //=> 75, 60 console.log(offsetX, offsetY); //=> 75, 40 ``` #### Signature Calculates the straight line path between two points. ###### Parameters * `[0].sourceX: number` The `x` position of the source handle. * `[0].sourceY: number` The `y` position of the source handle. * `[0].targetX: number` The `x` position of the target handle. * `[0].targetY: number` The `y` position of the target handle. ###### Returns `[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]` #### Notes * This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once. ### getViewportForBounds() [Source on Github](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/general.ts/#L293-L294) This util returns the viewport for the given bounds. You might use this to pre-calculate the viewport for a given set of nodes on the server or calculate the viewport for the given bounds *without* changing the viewport directly. ```js import { getViewportForBounds } from '@xyflow/svelte'; const { x, y, zoom } = getViewportForBounds( { x: 0, y: 0, width: 100, height: 100, }, 1200, 800, 0.5, 2, ); ``` #### Signature Returns a viewport that encloses the given bounds with padding. ###### Parameters * `bounds: Rect` Bounds to fit inside viewport. * `width: number` Width of the viewport. * `height: number` Height of the viewport. * `minZoom: number` Minimum zoom level of the resulting viewport. * `maxZoom: number` Maximum zoom level of the resulting viewport. * `padding: Padding` Padding around the bounds. ###### Returns * `x: number` * `y: number` * `zoom: number` #### Notes * This is quite a low-level utility. You might want to look at the [`fitView`](/api-reference/hooks/use-svelte-flow#fitview) or [`fitBounds`](/api-reference/hooks/use-svelte-flow#fitbounds) methods for a more practical api. ### Utils ### isEdge() [Source on GitHub](https://github.com/xyflow/xyflow/blob/v11/packages/core/src/utils/graph.ts/#L19) Test whether an object is usable as an [`Edge`](/api-reference/types/edge). In TypeScript this is a type guard that will narrow the type of whatever you pass in to [`Edge`](/api-reference/types/edge) if it returns `true`. ```js import { isEdge } from '@xyflow/svelte'; const edge = { id: 'edge-a', source: 'a', target: 'b', }; if (isEdge(edge)) { // .. } ``` #### Signature Test whether an object is usable as an Edge ###### Parameters * `element: unknown` The element to test ###### Returns `boolean` ### isNode() [Source on GitHub](https://github.com/xyflow/xyflow/blob/v11/packages/core/src/utils/graph.ts/#L22) Test whether an object is usable as an [`Node`](/api-reference/types/node). In TypeScript this is a type guard that will narrow the type of whatever you pass in to [`Node`](/api-reference/types/node) if it returns `true`. ```js import { isNode } from '@xyflow/svelte'; const node = { id: 'node-a', data: { label: 'node', }, position: { x: 0, y: 0, }, }; if (isNode(node)) { // .. } ``` #### Signature Test whether an object is usable as a Node ###### Parameters * `element: unknown` The element to test ###### Returns `boolean`