# 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 ### Quickstart If you want to get up and running as soon as possible, you're in the right place! 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. #### Dependency Svelte Flow is published on npm as [`@xyflow/svelte`](https://npmjs.com/package/@xyflow/svelte). ```bash copy npm2yarn npm install @xyflow/svelte ``` #### Play online You can try Svelte Flow without setting anything up locally by checking out the starter projects we have on [Stackblitz](https://stackblitz.com): JS } /> TS } /> #### Vite template If you want to get started right away, you can use our [vite template](https://github.com/xyflow/vite-svelte-flow-template): ```bash copy npm2yarn npx degit xyflow/vite-svelte-flow-template app-name ``` #### Project Setup To get started locally, you should have a few things: * [Node.js](https://nodejs.org/en/) installed. * Either npm or another package manager like [yarn](https://yarnpkg.com/) or [pnpm](https://pnpm.io/). * Some knowledge of [Svelte](https://svelte.dev/). You don't need to be an expert, but you should be comfortable with the basics. 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 ``` #### Creating your first flow The `@xyflow/svelte` package exports the `` component, which is the entrypoint for you 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
``` ### 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/getting-started/key-concepts). 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)); ``` ### 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: guides/custom-edges ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte ``` ##### index.css ```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')!, }); ``` #### Adding an edge label 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! Because Svelte Flows edges are mounted inside a SVG component, we need to escape it's 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: guides/custom-edges-button ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte ``` ##### index.css ```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: guides/custom-edges-svg-path ##### App.svelte ```svelte ``` ##### SineEdge.svelte ```svelte ``` ##### StepEdge.svelte ```svelte ``` ##### index.css ```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). #### Creating 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: guides/custom-nodes ##### App.svelte ```svelte ``` ##### TextUpdaterNode.svelte ```svelte
{ updateNodeData(id, { text: evt.target.value }); }} class="nodrag" />
``` ##### index.css ```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; border: 1px solid #eee; padding: 5px; border-radius: 5px; background: white; } .text-updater-node label { display: block; color: #777; 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')!, }); ``` #### Adding Handles Svelte Flow provides a [`Handle`](/api-reference/components/handle) component that can be used to add handles to your custom nodes. It's as easy as mounting the component. ```svelte filename="CustomNode.svelte" highlight="Handle" ``` ###### Multiple Handles If you need more than just one source and target handle, you can use the `id` ### 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: guides/theming/a ##### 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: guides/theming/b ##### 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: | Class name | Description | | :---------------------------------- | :--------------------------------------------------------------------------------------- | | `.svelte-flow` | The outermost container | | `.svelte-flow__renderer` | The inner container | | `.svelte-flow__zoompane` | Zoom & pan pane | | `.svelte-flow__selectionpane` | Selection pane | | `.svelte-flow__selection` | User selection | | `.svelte-flow__edges` | The element containing all edges in the flow | | `.svelte-flow__edge` | Applied to each [`Edge`](/api-reference/types/edge) in the flow | | `.svelte-flow__edge.selected` | Added to an [`Edge`](/api-reference/types/edge) when selected | | `.svelte-flow__edge.animated` | Added to an [`Edge`](/api-reference/types/edge) when its `animated` prop is `true` | | `.svelte-flow__edge-path` | The SVG `` element of an [`Edge`](/api-reference/types/edge) | | `.svelte-flow__edge-label` | The edge label | | `.svelte-flow__connection` | Applied to the current connection line | | `.svelte-flow__connection-path` | The SVG `` of a connection line | | `.svelte-flow__nodes` | The element containing all nodes in the flow | | `.svelte-flow__node` | Applied to each [`Node`](/api-reference/types/node) in the flow | | `.svelte-flow__node.selected` | Added to a [`Node`](/api-reference/types/node) when selected. | | `.svelte-flow__node-default` | Added when [`Node`](/api-reference/types/node) type is `"default"` | | `.svelte-flow__node-input` | Added when [`Node`](/api-reference/types/node) type is `"input"` | | `.svelte-flow__node-output` | Added when [`Node`](/api-reference/types/node) type is `"output"` | | `.svelte-flow__node-group` | Added when [`Node`](/api-reference/types/node) type is `"group"` | | `.svelte-flow__nodesselection` | Nodes selection | | `.svelte-flow__nodesselection-rect` | Nodes selection rect | | `.svelte-flow__handle` | Applied to each [``](/api-reference/components/handle) component | | `.svelte-flow__handle-top` | Applied when a handle's [`Position`](/api-reference/types/position) is set to `"top"` | | `.svelte-flow__handle-right` | Applied when a handle's [`Position`](/api-reference/types/position) is set to `"right"` | | `.svelte-flow__handle-bottom` | Applied when a handle's [`Position`](/api-reference/types/position) is set to `"bottom"` | | `.svelte-flow__handle-left` | Applied when a handle's [`Position`](/api-reference/types/position) is set to `"left"` | | `.svelte-flow__handle.connecting` | Applied when a connection line is above a handle. | | `.svelte-flow__handle.valid` | Applied when a connection line is above a handle **and** the connection is valid | | `.svelte-flow__background` | Applied to the [``](/api-reference/components/background) component | | `.svelte-flow__minimap` | Applied to the [``](/api-reference/components/mini-map) component | | `.svelte-flow__controls` | Applied to the [``](/api-reference/components/controls) component | 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! ##### 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: | Variable name | Default | | :---------------------------------------------------- | :---------------------------------- | | `--xy-edge-stroke-default` | `#b1b1b7` | | `--xy-edge-stroke-width-default` | `1` | | `--xy-edge-stroke-selected-default` | `#555` | | `--xy-connectionline-stroke-default` | `#b1b1b7` | | `--xy-connectionline-stroke-width-default` | `1` | | `--xy-attribution-background-color-default` | `rgba(255, 255, 255, 0.5)` | | `--xy-minimap-background-color-default` | `#fff` | | `--xy-background-pattern-dot-color-default` | `#91919a` | | `--xy-background-pattern-line-color-default` | `#eee` | | `--xy-background-pattern-cross-color-default` | `#e2e2e2` | | `--xy-node-color-default` | `inherit` | | `--xy-node-border-default` | `1px solid #1a192b` | | `--xy-node-background-color-default` | `#fff` | | `--xy-node-group-background-color-default` | `rgba(240, 240, 240, 0.25)` | | `--xy-node-boxshadow-hover-default` | `0 1px 4px 1px rgba(0, 0, 0, 0.08)` | | `--xy-node-boxshadow-selected-default` | `0 0 0 0.5px #1a192b` | | `--xy-handle-background-color-default` | `#1a192b` | | `--xy-handle-border-color-default` | `#fff` | | `--xy-selection-background-color-default` | `rgba(0, 89, 220, 0.08)` | | `--xy-selection-border-default` | `1px dotted rgba(0, 89, 220, 0.8)` | | `--xy-controls-button-background-color-default` | `#fefefe` | | `--xy-controls-button-background-color-hover-default` | `#f4f4f4` | | `--xy-controls-button-color-default` | `inherit` | | `--xy-controls-button-color-hover-default` | `inherit` | | `--xy-controls-button-border-color-default` | `#eee` | | `--xy-controls-box-shadow-default` | `0 0 2px 1px rgba(0, 0, 0, 0.08)` | 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; } ``` ##### Tailwind 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)! ### Building a Flow Ready to create your first flow? This guide will walk you through the process step by step. If you haven't reviewed our [Key Concepts](/learn/getting-started/key-concepts) yet, we recommend doing that first. #### Getting Started First, import the Svelte Flow Component and its required styles into your project. We'll also import the `Background` component for visual enhancement. ```svelte ``` Next, render the main component inside an element with defined dimensions and place the `Background` component inside `SvelteFlow`. Content inside `SvelteFlow` stays fixed on top of the viewport. The `Background` component transforms its pattern to match viewport movement. ```svelte
``` If everything is set up correctly, you should see a blank canvas like this: Example: guides/getting-started/a ##### App.svelte ```svelte
``` ##### index.css ```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. Create an array of [node objects](/api-reference/types/node) with these **required** properties: * `id`: A unique identifier for each node * `position`: The x and y coordinates * `data`: An object for storing custom data We'll use the [`$state.raw`](https://svelte.dev/docs/svelte/$state) rune to make the array reactive. Simply using `$state` would **not only** make the array reactive, but **every property of each node**, too. This could lead to [performance issues](https://github.com/sveltejs/svelte/issues/11851), so we use [`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) instead. ```js let nodes = $state.raw([ { id: '1', position: { x: 0, y: 0 }, data: { label: 'Hello' }, }, { id: '2', position: { x: 100, y: 100 }, data: { label: 'World' }, }, ]); ``` Next, we [bind](https://svelte.dev/docs/svelte/$bindable) the nodes to the `SvelteFlow` component. This two-way binding allows both the component and your code to modify the nodes. ```svelte ``` If you've followed these steps, you should see a flow that looks like this: Example: guides/getting-started/b ##### App.svelte ```svelte
``` ##### index.css ```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 edges Let's connect the nodes with an edge. Initialize a [`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) with an array of [edge objects](/api-reference/types/edge) that have these **required** properties: * `id`: A unique identifier for the edge * `source`: The ID of the source node * `target`: The ID of the target node ```js let edges = $state.raw([{ id: 'e1-2', source: '1', target: '2' }]); ``` As with nodes, we [bind](https://svelte.dev/docs/svelte/$bindable) the edges to the `SvelteFlow` component. ```svelte ``` Your flow should now look like this: Example: guides/getting-started/c ##### App.svelte ```svelte
``` ##### index.css ```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')!, }); ``` #### Polishing the flow This all might already go into the right drection but it still looks a little bland and lopsided, doesn't it? ###### `fitView` Add the [`fitView`](/api-reference/svelte-flow#fitview) prop to automatically fit the initial viewport to the visible nodes. ```svelte ``` ###### Built-in node types Let's change the `type` of the first node to `input` and the second node to `output`. These are built-in node types, that come with a different set of handles. ```js let nodes = $state.raw([ { id: '1', type: 'input', position: { x: 0, y: 0 }, data: { label: 'Hello' }, }, { id: '2', type: 'output', position: { x: 100, y: 100 }, data: { label: 'World' }, }, ]); ``` ###### Built-in edge types We change the edge to type `smoothstep` and also give it a label! ```js let edges = $state.raw([ { id: 'e1-2', source: '1', target: '2', type: 'smoothstep', label: 'to the' }, ]); ``` #### Finished Flow And there you have it! Your completed flow should look like this: Example: guides/getting-started/d ##### App.svelte ```svelte
``` ##### index.css ```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: guides/built-in-components/minimap ##### App.svelte ```svelte { switch (node.type) { case 'input': return '#6ede87'; case 'output': return '#6865A5'; default: return '#ff0072'; } }} zoomable pannable /> ``` ##### 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')!, }); ``` #### 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: guides/built-in-components/controls ##### App.svelte ```svelte ``` ##### index.css ```css html, body, #app { width: 100%; height: 100%; margin: 0; font-family: sans-serif; } .svelte-flow { --xy-background-color: #f7f9fb; } ``` ##### 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: guides/built-in-components/background ##### App.svelte ```svelte
variant:
``` ##### index.css ```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: guides/built-in-components/panel ##### App.svelte ```svelte top-left top-center top-right bottom-left bottom-center bottom-right center-left center-right ``` ##### index.css ```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')!, }); ``` ### Installation and Requirements For this set-up we assume you already have node.js and npm, yarn or pnpm already installed. The Svelte Flow package is published under [`@xyflow/svelte`](https://www.npmjs.com/package/@xyflow/svelte) on npm and installable via: ```bash copy npm2yarn npm install @xyflow/svelte ``` Now you can import the Svelte Flow component and the styles in your application: ```js import { SvelteFlow } from '@xyflow/svelte'; import '@xyflow/svelte/dist/style.css'; ``` #### Hit the ground running To get folks building quickly, we have a template repository on GitHub that uses Vite and Svelte. You can find the template [here](https://github.com/xyflow/vite-svelte-flow-template). To use it, you can either create a new repository from the template, or use `degit` to grab the template's files without the git history: ```bash copy npx degit xyflow/vite-svelte-flow-template your-app-name ``` #### Prior Experience Needed Svelte Flow is a Svelte library. That means Svelte developers will feel comfortable using it. If basic Svelte terms and concepts like reactive state, props, components, and lifecycle methods are unfamiliar to you, you might need to learn more about Svelte before being able to use Svelte Flow fully. If you've never used Svelte before, we recommend first getting started on Svelte through the [official tutorial](https://svelte.dev/tutorial/svelte/welcome-to-svelte). ### Key Concepts 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 not found: /guides/concepts/] Here are the key terms you'll encounter when working with Svelte Flow: * **Svelte Flow Component**: The main component that renders your flowgraph * [**Svelte Flow Props**](/api-reference/svelte-flow): All configuration and data is passed through props * **Viewport**: The visible area of your flowgraph that can be panned and zoomed * When we say something is "in the viewport," it means it moves with the viewport's transformation * [**Base Styles**](/learn/customization/theming): The essential CSS required for Svelte Flow to function properly * [**Built-In Components**](/learn/getting-started/built-in-components): Ready-to-use components like Controls and MiniMap that enhance your flowgraph With these concepts in mind, you're ready to [install Svelte Flow](/learn/getting-started/installation) and start [building your first flow](/learn/getting-started/building-a-flow)! ### Layouting 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). That doesn't help very much if you don't know what the options *are*, so this guide is here to help! We'll split things up into resources for layouting nodes and resources for routing edges. To start, let's explore several different layouting libraries and how they can be used with Svelte Flow. #### Layouting Nodes For layouting nodes, there are a few third-party libraries that we think are worth checking out: | Library | Dynamic node sizes | Sub-flow layouting | Edge routing | Bundle size | | -------------------------------------------------- | ------------------ | ------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | [Dagre](https://github.com/dagrejs/dagre) | Yes | YesΒΉ | No | | | [D3-Hierarchy](https://github.com/d3/d3-hierarchy) | No | No | No | | | [D3-Force](https://github.com/d3/d3-force) | Yes | No | No | | | [ELK](https://github.com/kieler/elkjs) | Yes | Yes | Yes | | ΒΉ 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: guides/layouting/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: guides/layouting/d3 ##### 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: guides/layouting/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: guides/layouting/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: guides/subflows/a ##### App.svelte ```svelte ``` ##### index.css ```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#any-node-can-be-a-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: guides/subflows/b ##### App.svelte ```svelte ``` ##### index.css ```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: guides/subflows/c ##### App.svelte ```svelte ``` ##### index.css ```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](/support-us). **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 ### Click Connect You can create a new connection by clicking on a handle one by one. You can control this functionality via the [`clickConnect`](/api-reference/svelte-flow#clickconnect) prop on the `` component. Example: examples/edges/click-connect ##### AnnotationNode.svelte ```svelte
{data.label}
{#if data.arrowStyle}
β€Ή
{/if} ``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } /* 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; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: 'annotation-1', type: 'annotation', draggable: false, selectable: false, data: { label: 'first click a handle', arrowStyle: 'left: 0; bottom: 0; transform: translate(0px, -25px) rotate(160deg) scale(-1, 1);', }, position: { x: 75, y: 30 }, }, { id: 'annotation-2', type: 'annotation', draggable: false, selectable: false, data: { label: 'then click another', arrowStyle: 'right: 0; bottom: 0; transform: translate(-10px, 0px) rotate(160deg) scale(1, -1);', }, position: { x: 35, y: 140 }, }, { id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'Node 2' }, position: { x: 200, y: 200 } }, ]; export const initialEdges: Edge[] = []; ``` ### Custom Connection Line This example demonstrates how to customize the connection line that appears while dragging from a handle. You can modify the appearance and behavior of the connection line by implementing a custom connection line component. Example: examples/edges/custom-connectionline ##### App.svelte ```svelte ``` ##### ConnectionLine.svelte ```svelte {#if connection.current.inProgress} {/if} ``` ##### CustomNode.svelte ```svelte
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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Custom Edges This example shows how to create and use custom edge types in Svelte Flow. You can define your own edge components with custom rendering, animations, and interactions. Custom edges are useful for creating specialized visualizations or implementing complex edge behaviors. Example: examples/edges/custom-edges ##### App.svelte ```svelte ``` ##### BiDirectionalEdge.svelte ```svelte ``` ##### BiDirectionalNode.svelte ```svelte {data?.label} ``` ##### ButtonEdge.svelte ```svelte ``` ##### SelfConnectingEdge.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; font-family: sans-serif; box-sizing: border-box; } #app { width: 100vw; height: 100vh; } .button-edge__label { position: absolute; pointer-events: all; transform-origin: center; } .button-edge__button { width: 30px; height: 30px; border: 5px solid #f7f9fb; color: var(--xy-edge-label-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; } ``` ##### nodes-and-edges.ts ```ts import { type Node, type Edge, Position, MarkerType } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: 'button-1', type: 'input', data: { label: 'Button Edge 1' }, position: { x: 125, y: 0 }, }, { id: 'button-2', data: { label: 'Button Edge 2' }, position: { x: 125, y: 200 }, }, { id: 'bi-1', data: { label: 'Bi Directional 1' }, position: { x: 0, y: 300 }, type: 'bidirectional', sourcePosition: Position.Right, targetPosition: Position.Left, }, { id: 'bi-2', data: { label: 'Bi Directional 2' }, position: { x: 250, y: 300 }, type: 'bidirectional', sourcePosition: Position.Right, targetPosition: Position.Left, }, { id: 'self-1', data: { label: 'Self Connecting' }, position: { x: 125, y: 500 }, sourcePosition: Position.Right, targetPosition: Position.Left, }, ]; export const initialEdges: Edge[] = [ { id: 'edge-button', source: 'button-1', target: 'button-2', type: 'buttonedge', }, { id: 'edge-bi-1', source: 'bi-1', target: 'bi-2', type: 'bidirectional', sourceHandle: 'right', targetHandle: 'left', markerEnd: { type: MarkerType.ArrowClosed }, }, { id: 'edge-bi-2', source: 'bi-2', target: 'bi-1', type: 'bidirectional', sourceHandle: 'left', targetHandle: 'right', markerEnd: { type: MarkerType.ArrowClosed }, }, { id: 'edge-self', source: 'self-1', target: 'self-1', type: 'selfconnecting', markerEnd: { type: MarkerType.Arrow }, }, ]; ``` ### Edge Labels This example demonstrates how to add and customize labels on edges in Svelte Flow. Edge labels can be used to display additional information about connections, such as weights, types, or descriptions. You can style and position labels to match your visualization needs. Example: examples/edges/edge-labels ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte
{data.label}
``` ##### CustomEdgeStartEnd.svelte ```svelte {#if data.startLabel}
store.handleEdgeSelection(id)} style:transform={`translate(-50%, 0%) translate(${sourceX}px,${sourceY}px)`} class="edge-label nodrag nopan" > {data.startLabel}
{/if} {#if data.endLabel}
store.handleEdgeSelection(id)} style:transform={`translate(-50%, -100%) translate(${targetX}px,${targetY}px)`} class="edge-label nodrag nopan" > {data.endLabel}
{/if}
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 300 } }, { id: '3', data: { label: 'Node 3' }, position: { x: 200, y: 0 } }, { id: '4', data: { label: 'Node 4' }, position: { x: 200, y: 300 } }, ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', data: { label: 'edge label', }, type: 'custom', }, { id: 'e3-4', source: '3', target: '4', data: { startLabel: 'start edge label', endLabel: 'end edge label', }, type: 'start-end', }, ]; ``` ### Edge Markers This example shows how to add markers to the start and end of edges in Svelte Flow. Edge markers are useful for indicating direction, type, or status of connections. You can use SVG markers to create custom arrowheads or other visual indicators. Example: examples/edges/edge-markers ##### App.svelte ```svelte ``` ##### CustomEdgeMarker.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import { MarkerType, type Edge, type Node } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: 'A', position: { x: 20, y: 20 }, data: { label: 'A' } }, { id: 'B', position: { x: 100, y: 200 }, data: { label: 'B' } }, { id: 'C', position: { x: 300, y: 20 }, data: { label: 'C' } }, { id: 'D', position: { x: 300, y: 170 }, data: { label: 'D' } }, { id: 'E', position: { x: 250, y: 300 }, data: { label: 'E' } }, { id: 'F', position: { x: 250, y: 450 }, data: { label: 'F' } }, { id: 'G', position: { x: 20, y: 450 }, data: { label: 'G' } } ]; export const initialEdges: Edge[] = [ { id: 'A->B', source: 'A', target: 'B', markerEnd: { type: MarkerType.Arrow }, label: 'default arrow' }, { id: 'C->D', source: 'C', target: 'D', markerEnd: { type: MarkerType.ArrowClosed }, label: 'default closed arrow' }, { id: 'D->E', source: 'D', target: 'E', markerEnd: { type: MarkerType.ArrowClosed }, markerStart: { type: MarkerType.ArrowClosed, orient: 'auto-start-reverse' }, label: 'marker start and marker end' }, { id: 'E->F', source: 'E', target: 'F', markerEnd: 'logo', label: 'custom marker' }, { id: 'B->G', source: 'B', target: 'G', markerEnd: { type: MarkerType.ArrowClosed, width: 20, height: 20, color: '#FF4000' }, label: 'marker size and color', style: 'stroke-width: 2px; stroke: #FF4000' } ]; ``` ### Edge Types Svelte Flow comes with several built-in edge types: `default` (bezier), `straight`, `step`, and `smoothstep`. This example demonstrates how to use these different edge types and how to switch between them. Each edge type has its own characteristics and is suitable for different visualization needs. Example: examples/edges/edge-types ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Floating Edges This example demonstrates how to create floating edges that connect nodes to the viewport or other fixed points. Floating edges are useful for creating connections to external elements or implementing special edge behaviors that don't require a target node. Example: examples/edges/floating-edges ##### App.svelte ```svelte ``` ##### CustomNode.svelte ```svelte
{data.label}
``` ##### SimpleFloatingEdge.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import { type Node, type Edge, MarkerType } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', position: { x: 0, y: 0 }, data: { label: '🌞' }, type: 'custom' }, { id: '2', position: { x: 0, y: 150 }, data: { label: '🌎' }, type: 'custom' } ]; export const initialEdges: Edge[] = [ { id: '1-2', source: '1', target: '2', sourceHandle: 'c', targetHandle: 'a', type: 'floating', markerEnd: { type: MarkerType.ArrowClosed } } ]; ``` ##### utils.ts ```ts import { Position, type InternalNode } from '@xyflow/svelte'; // returns the position (top,right,bottom or right) passed node compared to function getParams(nodeA: InternalNode, nodeB: InternalNode): [number, number, Position] { const centerA = getNodeCenter(nodeA); const centerB = getNodeCenter(nodeB); const horizontalDiff = Math.abs(centerA.x - centerB.x); const verticalDiff = Math.abs(centerA.y - centerB.y); let position: Position; // when the horizontal difference between the nodes is bigger, we use Position.Left or Position.Right for the handle if (horizontalDiff > verticalDiff) { position = centerA.x > centerB.x ? Position.Left : Position.Right; } else { // here the vertical difference between the nodes is bigger, so we use Position.Top or Position.Bottom for the handle position = centerA.y > centerB.y ? Position.Top : Position.Bottom; } const [x, y] = getHandleCoordsByPosition(nodeA, position); return [x, y, position]; } function getHandleCoordsByPosition(node: InternalNode, handlePosition: Position): [number, number] { // all handles are from type source, that's why we use handleBounds.source here const handle = node.internals.handleBounds?.source?.find((h) => h.position === handlePosition); if (!handle?.width || !handle?.height) { return [0, 0]; } let offsetX = handle.width / 2; let offsetY = handle.height / 2; // this is a tiny detail to make the markerEnd of an edge visible. // The handle position that gets calculated has the origin top-left, so depending which side we are using, we add a little offset // when the handlePosition is Position.Right for example, we need to add an offset as big as the handle itself in order to get the correct position switch (handlePosition) { case Position.Left: offsetX = 0; break; case Position.Right: offsetX = handle.width; break; case Position.Top: offsetY = 0; break; case Position.Bottom: offsetY = handle.height; break; } const x = node.internals.positionAbsolute.x + handle.x + offsetX; const y = node.internals.positionAbsolute.y + handle.y + offsetY; return [x, y]; } function getNodeCenter(node: InternalNode) { return { x: node.internals.positionAbsolute.x + (node.measured.width ?? 0) / 2, y: node.internals.positionAbsolute.y + (node.measured.height ?? 0) / 2 }; } // returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge export function getEdgeParams(source: InternalNode, target: InternalNode) { const [sx, sy, sourcePos] = getParams(source, target); const [tx, ty, targetPos] = getParams(target, source); return { sx, sy, tx, ty, sourcePos, targetPos }; } ``` ### Reconnect Edge You can make edges reconnectable by using the `` component. This component is used to create a reconnection point on your custom edges. They behave similar to handles: 1. You can start dragging on an `` 2. This starts a new connection process and from the oppsite side of the edge 3. You can finish the connection the same way as it had been started from a handle Example: examples/edges/reconnect-edge ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte {#if !reconnecting} Select the edge and drag the ends to reconnect {/if} {#if selected} {/if} ``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 }, }, { id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 200 } }, { id: '3', data: { label: 'Node 3' }, position: { x: 200, y: 0 }, }, { id: '4', data: { label: 'Node 4' }, position: { x: 200, y: 200 } }, ]; export const initialEdges: Edge[] = [ { id: 'e1-4', source: '1', target: '4', type: 'custom', data: { label: 'reconnectable edge', }, }, ]; ``` ### Computing Flows This example shows how to compute and visualize different paths between nodes in your flow. You can use this to implement features like dependency analysis, path finding, or flow validation. The example demonstrates how to use Svelte Flow's built-in utilities to calculate paths and highlight them in the flow. Example: examples/interaction/computing-flows ##### App.svelte ```svelte
``` ##### ResultNode.svelte ```svelte
incoming texts:
{#if nodesData.current.length === 0}
no connected nodes
{:else} {#each nodesData.current as nodeData}
{nodeData.data.text}
{/each} {/if}
``` ##### TextNode.svelte ```svelte
text
updateNodeData(id, { text: evt.currentTarget.value })} />
``` ##### UppercaseNode.svelte ```svelte
uppercase transform
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Context Menu You can add a custom context menu to nodes and edges by using the `onNodeContextMenu` and `onEdgeContextMenu` events. This example shows how to implement a context menu that appears when right-clicking on nodes or edges. Example: examples/interaction/context-menu ##### App.svelte ```svelte
{#if menu} {/if}
``` ##### ContextMenu.svelte ```svelte
e.stopPropagation()} >

node: {id}

``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', position: { x: 175, y: 0 }, data: { label: 'a' } }, { id: '2', position: { x: 0, y: 250 }, data: { label: 'b' } }, { id: '3', position: { x: 175, y: 250 }, data: { label: 'c' } }, { id: '4', position: { x: 350, y: 250 }, data: { label: 'd' } } ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2' }, { id: 'e1-3', source: '1', target: '3' }, { id: 'e1-4', source: '1', target: '4' } ]; ``` ### Contextual Zoom This example demonstrates how to implement contextual zooming in Svelte Flow. You can zoom to specific nodes or areas of the flow using the `fitView` function. This is useful for focusing on particular parts of your flow or implementing navigation features. Example: examples/interaction/contextual-zoom ##### App.svelte ```svelte ``` ##### ZoomNode.svelte ```svelte
{#if showContent}

{data.content}

{:else}
{/if}
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'zoom', data: { content: 'zoom in/out to toggle content and placeholder', zoomLevel: 1.5 }, position: { x: 0, y: 0 } }, { id: '2', type: 'zoom', data: { content: 'zoom out some more', zoomLevel: 1.0 }, position: { x: 0, y: 100 } }, { id: '3', type: 'zoom', data: { content: 'zoom out even further', zoomLevel: 0.7 }, position: { x: 0, y: 200 } } ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', animated: true }, { id: 'e2-3', source: '2', target: '3', animated: true } ]; ``` ### Drag and Drop A drag and drop user interface is very common for node-based workflow editors. The drag and drop behavior outside of the Svelte Flow pane is not built in but can be implemented with the native [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) (used in this example) or a third party library like [Neodrag](https://www.neodrag.dev/docs/svelte). Example: examples/interaction/drag-and-drop ##### App.svelte ```svelte ``` ##### DnDProvider.svelte ```svelte {@render children()} ``` ##### Flow\.svelte ```svelte
``` ##### Sidebar.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Validation Custom nodes need to have at least one [`Handle` component](/api-reference/components/handle) to be connectable. You can pass a validation function [`isValidConnection`](/api-reference/svelte-flow#isvalidconnection) to the SvelteFlow component in order to check if a new connection is valid and should be added. Example: examples/interaction/validation ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Dagre Layout This example shows how to use [Dagre](https://github.com/dagrejs/dagre) for automatic hierarchical graph layout in Svelte Flow. Dagre is a JavaScript library that can automatically arrange nodes in a hierarchical structure, making it perfect for creating tree-like diagrams and flowcharts. Example: examples/layout/dagre ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; const position = { x: 0, y: 0 }; const edgeType = 'smoothstep'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'input' }, position }, { id: '2', data: { label: 'node 2' }, position }, { id: '2a', data: { label: 'node 2a' }, position }, { id: '2b', data: { label: 'node 2b' }, position }, { id: '2c', data: { label: 'node 2c' }, position }, { id: '2d', data: { label: 'node 2d' }, position }, { id: '3', data: { label: 'node 3' }, position }, { id: '4', data: { label: 'node 4' }, position }, { id: '5', data: { label: 'node 5' }, position }, { id: '6', type: 'output', data: { label: 'output' }, position }, { id: '7', type: 'output', data: { label: 'output' }, position } ]; export const initialEdges: Edge[] = [ { id: 'e12', source: '1', target: '2', type: edgeType, animated: true }, { id: 'e13', source: '1', target: '3', type: edgeType, animated: true }, { id: 'e22a', source: '2', target: '2a', type: edgeType, animated: true }, { id: 'e22b', source: '2', target: '2b', type: edgeType, animated: true }, { id: 'e22c', source: '2', target: '2c', type: edgeType, animated: true }, { id: 'e2c2d', source: '2c', target: '2d', type: edgeType, animated: true }, { id: 'e45', source: '4', target: '5', type: edgeType, animated: true }, { id: 'e56', source: '5', target: '6', type: edgeType, animated: true }, { id: 'e57', source: '5', target: '7', type: edgeType, animated: true } ]; ``` ### ELK.js Layout This example demonstrates how to use [ELK.js](https://github.com/kieler/elkjs) for automatic graph layout in Svelte Flow. ELK.js is a powerful layout engine that can automatically arrange nodes and edges in various ways, such as hierarchical, force-directed, or circular layouts. Example: examples/layout/elkjs ##### App.svelte ```svelte ``` ##### Flow\.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; const position = { x: 0, y: 0 }; const edgeType = 'smoothstep'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'input' }, position }, { id: '2', data: { label: 'node 2' }, position }, { id: '2a', data: { label: 'node 2a' }, position }, { id: '2b', data: { label: 'node 2b' }, position }, { id: '2c', data: { label: 'node 2c' }, position }, { id: '2d', data: { label: 'node 2d' }, position }, { id: '3', data: { label: 'node 3' }, position }, { id: '4', data: { label: 'node 4' }, position }, { id: '5', data: { label: 'node 5' }, position }, { id: '6', type: 'output', data: { label: 'output' }, position }, { id: '7', type: 'output', data: { label: 'output' }, position } ]; export const initialEdges: Edge[] = [ { id: 'e12', source: '1', target: '2', type: edgeType, animated: true }, { id: 'e13', source: '1', target: '3', type: edgeType, animated: true }, { id: 'e22a', source: '2', target: '2a', type: edgeType, animated: true }, { id: 'e22b', source: '2', target: '2b', type: edgeType, animated: true }, { id: 'e22c', source: '2', target: '2c', type: edgeType, animated: true }, { id: 'e2c2d', source: '2c', target: '2d', type: edgeType, animated: true }, { id: 'e45', source: '4', target: '5', type: edgeType, animated: true }, { id: 'e56', source: '5', target: '6', type: edgeType, animated: true }, { id: 'e57', source: '5', target: '7', type: edgeType, animated: true } ]; ``` ### Horizontal Flow This example demonstrates how to create a horizontal flow layout in Svelte Flow. Instead of the default top-to-bottom flow, this layout arranges nodes and edges from left to right, which can be useful for certain types of diagrams or when you need to optimize for widescreen displays. Example: examples/layout/horizontal-flow ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import { Position, type Node, type Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: 'horizontal-1', sourcePosition: Position.Right, type: 'input', data: { label: 'Input' }, position: { x: 0, y: 80 } }, { id: 'horizontal-2', sourcePosition: Position.Right, targetPosition: Position.Left, data: { label: 'A Node' }, position: { x: 250, y: 0 } }, { id: 'horizontal-3', sourcePosition: Position.Right, targetPosition: Position.Left, data: { label: 'Node 3' }, position: { x: 250, y: 160 } }, { id: 'horizontal-4', sourcePosition: Position.Right, targetPosition: Position.Left, data: { label: 'Node 4' }, position: { x: 500, y: 0 } }, { id: 'horizontal-5', sourcePosition: Position.Top, targetPosition: Position.Bottom, data: { label: 'Node 5' }, position: { x: 500, y: 100 } }, { id: 'horizontal-6', sourcePosition: Position.Bottom, targetPosition: Position.Top, data: { label: 'Node 6' }, position: { x: 500, y: 230 } }, { id: 'horizontal-7', sourcePosition: Position.Right, targetPosition: Position.Left, data: { label: 'Node 7' }, position: { x: 750, y: 50 } }, { id: 'horizontal-8', sourcePosition: Position.Right, targetPosition: Position.Left, data: { label: 'Node 8' }, position: { x: 750, y: 300 } } ]; export const initialEdges: Edge[] = [ { id: 'horizontal-e1-2', source: 'horizontal-1', type: 'smoothstep', target: 'horizontal-2', animated: true }, { id: 'horizontal-e1-3', source: 'horizontal-1', type: 'smoothstep', target: 'horizontal-3', animated: true }, { id: 'horizontal-e1-4', source: 'horizontal-2', type: 'smoothstep', target: 'horizontal-4', label: 'edge label' }, { id: 'horizontal-e3-5', source: 'horizontal-3', type: 'smoothstep', target: 'horizontal-5', animated: true }, { id: 'horizontal-e3-6', source: 'horizontal-3', type: 'smoothstep', target: 'horizontal-6', animated: true }, { id: 'horizontal-e5-7', source: 'horizontal-5', type: 'smoothstep', target: 'horizontal-7', animated: true }, { id: 'horizontal-e6-8', source: 'horizontal-6', type: 'smoothstep', target: 'horizontal-8', animated: true } ]; ``` ### Node Collisions This example demonstrates how to automatically resolve node overlaps. When nodes are placed too close together or overlap, the algorithm detects these collisions and moves the nodes apart to maintain visual clarity. For a deep dive into collision detection algorithms check out our [blog post on node collisions](https://xyflow.com/blog/node-collision-detection-algorithms). We also created a [benchmark](https://github.com/xyflow/node-collision-algorithms) to compare the performance of different approaches and you can see them in action in the [playground](https://xyflow.com/node-collisions). Example: examples/layout/node-collisions ##### App.svelte ```svelte { nodes = resolveCollisions(nodes, { maxIterations: Infinity, overlapThreshold: 0.5, margin: 15 }); }}> ``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } html, body, #app { height: 100%; } .svelte-flow__node { display: flex; height: 50px; width: 50px; align-items: center; justify-content: center; border-radius: 0.5rem; background-color: hsl(0, 0%, 100%); padding: 1rem; box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); } .svelte-flow__handle { opacity: 0; pointer-events: none !important; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', position: { x: 624, y: 449 }, data: { label: 'πŸ‘' }, }, { id: '2', position: { x: 661, y: 519 }, data: { label: 'πŸ‘' }, }, { id: '3', position: { x: 554, y: 413 }, data: { label: 'πŸ‘' } }, { id: '4', position: { x: 700, y: 380 }, data: { label: 'πŸ‘' }, }, { id: '5', position: { x: 637, y: 309 }, data: { label: 'πŸ‘' }, }, { id: '6', position: { x: 630, y: 379 }, data: { label: 'πŸ‘' }, }, { id: '7', position: { x: 770, y: 435 }, data: { label: 'πŸ‘' } }, { id: '8', position: { x: 694, y: 449 }, data: { label: 'πŸ‘' }, }, { id: '9', position: { x: 707, y: 310 }, data: { label: 'πŸ‘' }, }, { id: '10', position: { x: 560, y: 343 }, data: { label: 'πŸ‘' } }, { id: '11', position: { x: 522, y: 487 }, data: { label: 'πŸ‘' } }, { id: '12', position: { x: 591, y: 518 }, data: { label: 'πŸ‘' }, }, { id: '13', position: { x: 701, y: 589 }, data: { label: 'πŸ‘' } }, { id: '14', position: { x: 731, y: 519 }, data: { label: 'πŸ‘' }, }, { id: '15', position: { x: 801, y: 505 }, data: { label: 'πŸ‘' } }, { id: '16', position: { x: 250, y: 450 }, data: { label: '🐢' } }, ]; export const initialEdges: Edge[] = []; ``` ##### resolve-collisions.ts ```ts import type { Node } from '@xyflow/svelte'; export type CollisionAlgorithmOptions = { maxIterations: number; overlapThreshold: number; margin: number; }; export type CollisionAlgorithm = ( nodes: Node[], options: CollisionAlgorithmOptions, ) => Node[]; type Box = { x: number; y: number; width: number; height: number; moved: boolean; node: Node; }; function getBoxesFromNodes(nodes: Node[], margin = 0): Box[] { const boxes: Box[] = new Array(nodes.length); for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; boxes[i] = { x: node.position.x - margin, y: node.position.y - margin, width: (node.width ?? node.measured?.width ?? 0) + margin * 2, height: (node.height ?? node.measured?.height ?? 0) + margin * 2, node, moved: false, }; } return boxes; } export const resolveCollisions: CollisionAlgorithm = ( nodes, { maxIterations = 50, overlapThreshold = 0.5, margin = 0 }, ) => { const boxes = getBoxesFromNodes(nodes, margin); for (let iter = 0; iter <= maxIterations; iter++) { let moved = false; for (let i = 0; i < boxes.length; i++) { for (let j = i + 1; j < boxes.length; j++) { const A = boxes[i]; const B = boxes[j]; // Calculate center positions const centerAX = A.x + A.width * 0.5; const centerAY = A.y + A.height * 0.5; const centerBX = B.x + B.width * 0.5; const centerBY = B.y + B.height * 0.5; // Calculate distance between centers const dx = centerAX - centerBX; const dy = centerAY - centerBY; // Calculate overlap along each axis const px = (A.width + B.width) * 0.5 - Math.abs(dx); const py = (A.height + B.height) * 0.5 - Math.abs(dy); // Check if there's significant overlap if (px > overlapThreshold && py > overlapThreshold) { A.moved = B.moved = moved = true; // Resolve along the smallest overlap axis if (px < py) { // Move along x-axis const sx = dx > 0 ? 1 : -1; const moveAmount = (px / 2) * sx; A.x += moveAmount; B.x -= moveAmount; } else { // Move along y-axis const sy = dy > 0 ? 1 : -1; const moveAmount = (py / 2) * sy; A.y += moveAmount; B.y -= moveAmount; } } } } // Early exit if no overlaps were found if (!moved) { break; } } const newNodes = boxes.map((box) => { if (box.moved) { return { ...box.node, position: { x: box.x + margin, y: box.y + margin, }, }; } return box.node; }); return newNodes; }; ``` ### Subflows This example shows how to implement subflows in Svelte Flow. Subflows allow you to create nested or grouped flows within your main flow, which is useful for organizing complex diagrams into logical sections or implementing hierarchical structures. Example: examples/layout/subflows ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'Node 0' }, position: { x: 250, y: 5 }, class: 'light' }, { id: '2', data: { label: 'Group A' }, position: { x: 100, y: 100 }, class: 'light', style: 'background-color: rgba(255, 0, 0, 0.2); width: 200px; height: 200px;' }, { id: '2a', data: { label: 'Node A.1' }, position: { x: 10, y: 50 }, parentId: '2' }, { id: '3', data: { label: 'Node 1' }, position: { x: 320, y: 100 }, class: 'light' }, { id: '4', data: { label: 'Group B' }, position: { x: 320, y: 200 }, class: 'light', style: 'background-color: rgba(255, 0, 0, 0.2); width: 300px; height: 300px;', type: 'group' }, { id: '4a', data: { label: 'Node B.1' }, position: { x: 15, y: 65 }, class: 'light', parentId: '4', extent: 'parent' }, { id: '4b', data: { label: 'Group B.A' }, position: { x: 15, y: 120 }, class: 'light', style: 'background-color: rgba(255, 0, 255, 0.2); width: 270px; height: 150px;', parentId: '4' }, { id: '4b1', data: { label: 'Node B.A.1' }, position: { x: 20, y: 40 }, class: 'light', parentId: '4b' }, { id: '4b2', data: { label: 'Node B.A.2' }, position: { x: 100, y: 100 }, class: 'light', parentId: '4b' } ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', animated: true }, { id: 'e1-3', source: '1', target: '3' }, { id: 'e2a-4a', source: '2a', target: '4a' }, { id: 'e3-4b', source: '3', target: '4b' }, { id: 'e4a-4b1', source: '4a', target: '4b1' }, { id: 'e4a-4b2', source: '4a', target: '4b2' }, { id: 'e4b1-4b2', source: '4b1', target: '4b2' } ]; ``` ### Download Image This example demonstrates how to export your Svelte Flow diagram as an image. Learn how to capture the current view of your flow and save it as a PNG or other image format, which is useful for sharing, documentation, or printing your diagrams. The `version` of the [html-to-image](https://www.npmjs.com/package/html-to-image) package used in this example, has been locked to `1.11.11`, which is the latest working `version` for the package. The recent versions, after `1.11.11`, are not exporting images properly and there is open [issue](https://github.com/bubkoo/html-to-image/issues/516) for this on Github. Example: examples/misc/download-image ##### App.svelte ```svelte ``` ##### CustomNode.svelte ```svelte
β–Ό
``` ##### DownloadButton.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import { type Node, type Edge, Position } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'β–²' }, position: { x: 0, y: 50 }, sourcePosition: Position.Right, style: 'background-color: #BEE3F8;', }, { id: '2', type: 'custom', data: {}, position: { x: 200, y: 50 }, style: 'background-color: #90CDF4;', }, { id: '3', data: { label: 'β–²' }, position: { x: 400, y: 0 }, targetPosition: Position.Left, sourcePosition: Position.Right, style: 'background-color: #63B3ED;', }, { id: '4', data: { label: 'β–²' }, position: { x: 400, y: 100 }, targetPosition: Position.Left, sourcePosition: Position.Right, style: 'background-color: #63B3ED;', }, { id: '5', type: 'output', data: { label: 'β–²' }, position: { x: 600, y: 0 }, targetPosition: Position.Left, style: 'background-color: #4299E1;', }, { id: '6', type: 'output', data: { label: 'β–²' }, position: { x: 600, y: 100 }, targetPosition: Position.Left, style: 'background-color: #4299E1;', }, ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', }, { id: 'e2a-3', source: '2', target: '3', sourceHandle: 'a', }, { id: 'e2b-4', source: '2', target: '4', sourceHandle: 'b', }, { id: 'e3a-5', source: '3', target: '5', }, { id: 'e4b-6', source: '4', target: '6', }, ]; ``` ### Feature Overview This example provides an overview of Svelte Flow's main features. See demonstrations of node types, edge types, interactions, layouts, and other capabilities in one comprehensive example. Perfect for understanding what's possible with Svelte Flow. Example: examples/misc/feature-overview ##### AnnotationNode.svelte ```svelte
{data.level}.
{data.label}
{#if data.arrowStyle}
β€Ή
{/if} ``` ##### App.svelte ```svelte node.type} /> ``` ##### ButtonEdge.svelte ```svelte ``` ##### CircleNode.svelte ```svelte
{label || 'no node connected'}
``` ##### Message.svelte ```svelte
On the bottom left you see the Controls and the bottom right the{' '} MiniMap. This is also just a node πŸ₯³
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### 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: 200px; color: #683bfa; position: absolute; box-shadow: none; font-family: monospace; text-align: left; background-color: transparent; border: 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; } ``` ##### 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: { level: 1, label: 'Built-in node and edge types. Draggable, deletable and connectable!', arrowStyle: 'right: 0; bottom: 0; transform: translate(-30px,10px) rotate(-80deg);', }, position: { x: -200, y: -30 }, }, { id: '1-1', type: 'input', data: { label: 'Input Node', }, position: { x: 150, y: 0 }, }, { id: '1-2', type: 'default', data: { label: 'Default Node', }, position: { x: 0, y: 100 }, }, { id: '1-3', type: 'output', data: { label: 'Output Node', }, position: { x: 300, y: 100 }, }, { id: 'annotation-2', type: 'annotation', draggable: false, selectable: false, data: { level: 2, label: 'Sub flows, toolbars and resizable nodes!', arrowStyle: 'left: 0; bottom: 0; transform: translate(5px, 25px) scale(1, -1) rotate(100deg);', }, position: { x: 220, y: 200 }, }, { id: '2-1', type: 'group', position: { x: -170, y: 250, }, data: {}, style: 'width: 380px; height: 180px;', }, { id: '2-2', data: {}, type: 'tools', position: { x: 50, y: 50 }, style: 'width: 80px; height: 80px;', parentId: '2-1', extent: 'parent', }, { id: '2-3', type: 'resizer', data: { label: 'Resize Me', }, position: { x: 250, y: 50 }, style: 'width: 80px; height: 80px;', parentId: '2-1', extent: 'parent', }, { id: 'annotation-3', type: 'annotation', draggable: false, selectable: false, data: { level: 3, label: 'Nodes and edges can be anything and are fully customizable!', arrowStyle: 'right: 0; bottom: 0; transform: translate(-35px, 20px) rotate(-80deg);', }, position: { x: -40, y: 570 }, }, { id: '3-2', type: 'textinput', position: { x: 150, y: 650 }, data: {}, }, { id: '3-1', type: 'circle', position: { x: 350, y: 500 }, data: {}, }, ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1-1', target: '1-2', label: 'edge', type: 'smoothstep', }, { id: 'e1-3', source: '1-1', target: '1-3', animated: true, label: 'animated edge', }, { id: 'e2-2', source: '1-2', target: '2-2', type: 'smoothstep', markerEnd: { type: MarkerType.ArrowClosed, }, }, { id: 'e2-3', source: '2-2', target: '2-3', type: 'smoothstep', markerEnd: { type: MarkerType.ArrowClosed, }, }, { id: 'e3-3', source: '2-3', sourceHandle: 'a', target: '3-2', type: 'button', animated: true, style: 'stroke: rgb(158, 118, 255);', }, { id: 'e3-4', source: '2-3', sourceHandle: 'b', target: '3-1', type: 'button', }, ]; ``` ### Hello World This is a basic example that shows how to get started with Svelte Flow. Learn how to create a simple flow with nodes and edges, handle basic interactions, and understand the core concepts of Svelte Flow. Example: examples/misc/hello-world ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Threlte Flow This example shows how to integrate Svelte Flow with [Threlte](https://threlte.xyz/), a Svelte component library for Three.js. Learn how to create 3D visualizations of your flow graphs and add interactive 3D elements to your nodes and edges. Example: examples/misc/threlte-flow ##### App.svelte ```svelte ``` ##### ColorPickerNode.svelte ```svelte

{flowState.color}

``` ##### NodeWrapper.svelte ```svelte
{label}
``` ##### SliderNode.svelte ```svelte ``` ##### SwitcherNode.svelte ```svelte
{#each options as option} {/each}
``` ##### ThrelteNode.svelte ```svelte
``` ##### ThrelteScene.svelte ```svelte { ref.lookAt(0, 0, 0); }} /> {#key flowState.shape} {#each randomAssets as asset} {#if flowState.shape === 'cube'} {:else if flowState.shape === 'pyramid'} {/if} {/each} {/key} ``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } @tailwind base; @tailwind components; @tailwind utilities; ``` ##### nodes-and-edges.svelte.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export type FlowState = { color: string; zoom: number; shape: string; }; class FlowStateInstance { color = $state('#ff4000'); zoom = $state(17); shape = $state('cube'); } export const flowState = new FlowStateInstance(); export type NodeData = { label: string; }; export const initialNodes: Node[] = [ { id: 'hero', type: 'hero', position: { x: 390, y: 50 }, data: { label: 'output', }, class: 'w-[200px] lg:w-[300px]', }, { id: 'color', type: 'colorpicker', position: { x: 50, y: 0 }, data: { label: 'shape color', }, class: 'w-[150px]', }, { id: 'shape', type: 'switcher', position: { x: 0, y: 125 }, data: { label: 'shape type', }, class: 'w-[150px]', }, { id: 'zoom', type: 'slider', position: { x: 40, y: 280 }, data: { label: 'zoom level', }, class: 'w-[150px]', }, ]; const edgeStyle = 'stroke:#D2D2D2; stroke-width:2;'; export const initialEdges: Edge[] = [ { id: 'color->hero', source: 'color', target: 'hero', targetHandle: 'color', style: edgeStyle, animated: true, }, { id: 'shape->hero', source: 'shape', target: 'hero', targetHandle: 'shape', style: edgeStyle, animated: true, }, { id: 'zoom->hero', source: 'zoom', target: 'hero', targetHandle: 'zoom', style: edgeStyle, animated: true, }, ]; ``` ##### tailwind.config.js ```js /** @type {import('tailwindcss').Config} */ export default { // Needs to be important to override the default styles important: true, content: ['./src/**/*.{html,js,svelte,ts}'], theme: { extend: {} }, plugins: [] }; ``` ### Transitions This example shows how to use [Svelte's built-in transitions](https://svelte.dev/docs/svelte/svelte-transition) to animate creating and removing nodes and edges. Since nodes and edges are rendered as normal DOM elements, you can apply any Svelte transition to them. Note that you need to use the `|global` modifier with your transitions for them to work properly within Svelte Flow's component structure. Example: examples/misc/transitions ##### App.svelte ```svelte ``` ##### CustomEdge.svelte ```svelte ``` ##### CustomNode.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } html, body, #app { height: 100%; } .svelte-flow__node { width: 150px; } ``` ### useSvelteFlow This example demonstrates how to use the `useSvelteFlow` store and actions in your Svelte components. Learn how to access and manipulate the flow state, handle events, and implement custom functionality using Svelte Flow's built-in utilities. Example: examples/misc/use-svelte-flow ##### App.svelte ```svelte ``` ##### ControlButtons.svelte ```svelte
x: {viewport.current.x.toFixed(1)} y: {viewport.current.y.toFixed(1)} zoom: {viewport.current.zoom.toFixed( 1, )}
``` ##### Flow\.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Add Node On Edge Drop You can create a new node when you drop the connection line on the pane by using the `onConnectStart` and `onConnectEnd` handlers. Example: examples/nodes/add-node-on-edge-drop ##### App.svelte ```svelte ``` ##### Flow\.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Connection Limit This is an example of a custom node with a custom handle that can limit the amount of connections a handle can have using the `isConnectable` prop. You can use a boolean, a number (the number of max. connections the handle should have) or a callback function that returns a boolean as an arg for the `isConnectable` prop of the CustomHandle component. Example: examples/nodes/connection-limit ##### App.svelte ```svelte ``` ##### CustomNode.svelte ```svelte
{'← Only one edge allowed'}
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } .svelte-flow__node-custom .svelte-flow__handle.connectable { border-color: var(--xy-theme-selected); background: var(--xy-theme-selected); &:hover { background: var(--xy-theme-selected); } } ``` ##### nodes-and-edges.ts ```ts import { Position, type Node, type Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 0, y: 25 }, sourcePosition: Position.Right }, { id: '2', type: 'custom', data: {}, position: { x: 250, y: 50 } }, { id: '3', type: 'input', data: { label: 'Node 2' }, position: { x: 0, y: 100 }, sourcePosition: Position.Right } ]; export const initialEdges: Edge[] = []; ``` ### Custom Nodes Creating custom nodes is as easy as building a regular Svelte component and passing it to `nodeTypes`. Since they're standard Svelte components, you can display any content and implement any functionality you need. Plus, you'll have access to a range of props that allow you to extend and customize the default node behavior. For more details, check out our [Custom Nodes guide](/learn/customization/custom-nodes). Example: examples/nodes/custom-node ##### App.svelte ```svelte ``` ##### ColorSelectorNode.svelte ```svelte
Custom Color Picker Node: {bgColor.current}
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Delete Middle Node This example shows you how to recover deleted edges when you remove a node from the middle of a chain. In other words, if we have three nodes connected in sequence - `a->b->c` - and we deleted the middle node `b`, this example shows you how to end up with the graph `a->c`. To achieve this, we need to make use of a few bits: * The [`onbeforedelete`](/api-reference/svelte-flow#onbeforedelete) handler lets us intercept and modify the deletion process before nodes are removed. * [`getConnectedEdges`](/api-reference/utils/get-connected-edges) gives us all the edges connected to a node, either as source or target. * [`getIncomers`](/api-reference/utils/get-incomers) and [`getOutgoers`](/api-reference/utils/get-outgoers) give us the nodes connected to a node as source or target. All together, this allows us to take all the nodes connected to the deleted node, and reconnect them to any nodes the deleted node was connected to. Example: examples/nodes/delete-middle-node ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'Start here...' }, position: { x: -150, y: 0 } }, { id: '2', type: 'input', data: { label: '...or here!' }, position: { x: 150, y: 0 } }, { id: '3', data: { label: 'Delete me.' }, position: { x: 0, y: 100 } }, { id: '4', data: { label: 'Then me!' }, position: { x: 0, y: 200 } }, { id: '5', type: 'output', data: { label: 'End here!' }, position: { x: 0, y: 300 } } ]; export const initialEdges: Edge[] = [ { id: '1->3', source: '1', target: '3' }, { id: '2->3', source: '2', target: '3' }, { id: '3->4', source: '3', target: '4' }, { id: '4->5', source: '4', target: '5' } ]; ``` Although this example is less than 20 lines of code there's quite a lot to digest. Let's break some of it down: * Our `onbeforedelete` handler is called with an object containing `nodes` and `edges` arrays representing what will be deleted. The `nodes` array contains every node that will be deleted. If you select an individual node and press the delete key, it will contain just that node, but if you make a selection *all* the nodes in that selection will be included. * We create a new array of edges - `remainingEdges` - that contains all the edges in the flow that have nothing to do with the node(s) we're about to delete. * We create another array of edges by *flatMapping* over the array of `incomers`. These are nodes that were connected to the deleted node as a source. For each of these nodes, we create a new edge that connects to each node in the array of `outgoers`. These are nodes that were connected to the deleted node as a target. * Finally, we return `true` from the callback to allow the deletion to proceed with our modified edge array. For brevity, we're using object destructuring while at the same time renaming the variable bound (e.g. `({ id: source }) => ...)` destructures the `id` property of the object and binds it to a new variable called `source`) but you don't need to do this #### Quick Reference } /> } /> ### Drag Handle You can restrict dragging to a specific part of node, by specifying a class that will act as a [`dragHandle`](/api-reference/types/node). To prevent specific elements within a drag handle from triggering a drag, you can use the [`nodrag`](/api-reference/types/node-props#notes) class name on those elements. Example: examples/nodes/drag-handle ##### App.svelte ```svelte ``` ##### DragHandleNode.svelte ```svelte
Only draggable here β†’
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Easy Connect Fed up with tiny little connection handles? Make your whole node act as one! Keep in mind though that you need to define separate drag handles in this case to still be able to drag the node. Example: examples/nodes/easy-connect ##### App.svelte ```svelte ``` ##### CustomNode.svelte ```svelte
{#if !connection.current.inProgress} {/if} {label}
``` ##### FloatingEdge.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } .customNodeBody { width: 150px; height: 80px; position: relative; border-radius: 10px; display: flex; justify-content: center; align-items: center; font-weight: bold; } div.customHandle { width: 100%; height: 100%; background: blue; position: absolute; top: 0; left: 0; border-radius: 0; transform: none; border: none; opacity: 0; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'custom', position: { x: 0, y: 0 }, data: {}, }, { id: '2', type: 'custom', position: { x: 250, y: 320 }, data: {}, }, { id: '3', type: 'custom', position: { x: 40, y: 300 }, data: {}, }, { id: '4', type: 'custom', position: { x: 300, y: 0 }, data: {}, }, ]; export const initialEdges: Edge[] = []; ``` ##### utils.ts ```ts import { Position, MarkerType, type XYPosition, type InternalNode, } from '@xyflow/svelte'; // this helper function returns the intersection point // of the line between the center of the intersectionNode and the target node function getNodeIntersection( intersectionNode: InternalNode, targetNode: InternalNode, ) { // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a const intersectionNodePosition = intersectionNode.internals .positionAbsolute || { x: 0, y: 0 }; const targetPosition = targetNode.internals.positionAbsolute || { x: 0, y: 0, }; const w = (intersectionNode.measured.width ?? 0) / 2; const h = (intersectionNode.measured.height ?? 0) / 2; const x2 = intersectionNodePosition.x + w; const y2 = intersectionNodePosition.y + h; const x1 = targetPosition.x + (targetNode.measured.width ?? 0) / 2; const y1 = targetPosition.y + (targetNode.measured.height ?? 0) / 2; const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h); const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h); const a = 1 / (Math.abs(xx1) + Math.abs(yy1)); const xx3 = a * xx1; const yy3 = a * yy1; const x = w * (xx3 + yy3) + x2; const y = h * (-xx3 + yy3) + y2; return { x, y }; } // returns the position (top,right,bottom or right) passed node compared to the intersection point function getEdgePosition(node: InternalNode, intersectionPoint: XYPosition) { if (!node.measured.width || !node.measured.height) { return null; } const nx = Math.round(node.internals.positionAbsolute?.x ?? 0); const ny = Math.round(node.internals.positionAbsolute?.y ?? 0); const px = Math.round(intersectionPoint.x); const py = Math.round(intersectionPoint.y); if (px <= nx + 1) { return Position.Left; } if (px >= nx + node.measured.width - 1) { return Position.Right; } if (py <= ny + 1) { return Position.Top; } if (py >= ny + node.measured.height - 1) { return Position.Bottom; } return Position.Top; } // returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge export function getEdgeParams(source: InternalNode, target: InternalNode) { const sourceIntersectionPoint = getNodeIntersection(source, target); const targetIntersectionPoint = getNodeIntersection(target, source); const sourcePos = getEdgePosition(source, sourceIntersectionPoint); const targetPos = getEdgePosition(target, targetIntersectionPoint); return { sx: sourceIntersectionPoint.x, sy: sourceIntersectionPoint.y, tx: targetIntersectionPoint.x, ty: targetIntersectionPoint.y, sourcePos, targetPos, }; } ``` ### Intersections The `useSvelteFlow` hook exports [helpers to check intersections](/api-reference/hooks/use-svelte-flow#isnodeintersecting) of nodes and areas. In this example you can drag a node and get a visual feedback when it intersects with another node. Example: examples/nodes/intersections ##### App.svelte ```svelte ``` ##### Flow\.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } .svelte-flow.intersection-flow .svelte-flow__node.highlight { background-color: var(--xy-theme-selected) !important; color: white; } .svelte-flow.intersection-flow .svelte-flow__node { display: flex; justify-content: center; align-items: center; font-weight: 700; border-radius: 1px; border-width: 2px; box-shadow: 6px 6px 0 1px rgba(0, 0, 0, 0.7); } .svelte-flow.intersection-flow .svelte-flow__node.selected, .svelte-flow.intersection-flow .svelte-flow__node:hover, .svelte-flow.intersection-flow .svelte-flow__node:focus { box-shadow: 6px 6px 0 1px rgba(0, 0, 0, 0.7); background-color: #eee; } .svelte-flow.intersection-flow .svelte-flow__handle { display: none; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 }, style: 'width: 200px; height: 100px;' }, { id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 150 } }, { id: '3', data: { label: 'Node 3' }, position: { x: 250, y: 0 } }, { id: '4', data: { label: 'Node' }, position: { x: 350, y: 150 }, style: 'width: 50px; height: 50px;' } ]; export const initialEdges: Edge[] = []; ``` ### Node Resizer The [``](/api-reference/components/node-resizer) component can be used to add a resize UI for a custom node. The `svelteflow` package also exports a `` component for implementing a custom resizing UI as shown in this example. 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Proximity Connect This example shows how to automatically create edges when a node is dropped in close proximity to another one. While dragging, a dotted connection line is displayed to show which edge will be created if you drop the node. Example: examples/nodes/proximity-connect ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } .temp .svelte-flow__edge-path { stroke: #bbb; stroke-dasharray: 5 5; } .svelte-flow .svelte-flow__node { border-radius: 100%; background-color: #fff; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; } ``` ##### nodes-and-edges.ts ```ts import { Position, type Node, type Edge } from '@xyflow/svelte'; const nodeDefaults = { sourcePosition: Position.Right, targetPosition: Position.Left }; export const initialNodes: Node[] = [ { id: '1', position: { x: 0, y: 0 }, data: { label: '⬛️' }, ...nodeDefaults }, { id: '2', position: { x: 250, y: -100 }, data: { label: '🟩' }, ...nodeDefaults }, { id: '3', position: { x: 250, y: 100 }, data: { label: '🟧' }, ...nodeDefaults }, { id: '4', position: { x: 500, y: 0 }, data: { label: '🟦' }, ...nodeDefaults } ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2' }, { id: 'e1-3', source: '1', target: '3' } ]; ``` ### Stress Test You doubt we can render a lot of nodes and edges? See for yourself. Example: examples/nodes/stress ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### utils.js ```js export function createNodesAndEdges(xNodes = 10, yNodes = 10) { const nodes = []; const edges = []; let nodeId = 1; let recentNodeId = null; for (let y = 0; y < yNodes; y++) { for (let x = 0; x < xNodes; x++) { const position = { x: x * 100, y: y * 50 }; const data = { label: `Node ${nodeId}` }; const node = { id: `stress-${nodeId.toString()}`, style: 'width: 50px; fontSize: 11pt;', data, position }; nodes.push(node); if (recentNodeId && nodeId <= xNodes * yNodes) { edges.push({ id: `${x}-${y}`, source: `stress-${recentNodeId.toString()}`, target: `stress-${nodeId.toString()}` }); } recentNodeId = nodeId; nodeId++; } } return { nodes, edges }; } ``` ### Updating Nodes You can update properties of nodes and edges freely as long as you pass a newly created `nodes` or `edges` array to `SvelteFlow`. You have to create a new `data` object on a node to notify Svelte Flow about data changes.
Example: examples/nodes/update-node ##### App.svelte ```svelte label: background: hidden: ``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ### Base Style This example shows how to customize the base styling of Svelte Flow components. You can modify the appearance of nodes, edges, handles, and the background to match your application's design. Learn how to use CSS variables and custom styles to create a consistent look and feel. Example: examples/styling/base-style ##### App.svelte ```svelte ``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', position: { x: 0, y: 150 }, data: { label: 'base style 1' } }, { id: '2', position: { x: 250, y: 0 }, data: { label: 'base style 2' } }, { id: '3', position: { x: 250, y: 150 }, data: { label: 'base style 3' } }, { id: '4', position: { x: 250, y: 300 }, data: { label: 'base style 4' } } ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2' }, { id: 'e1-3', source: '1', target: '3' }, { id: 'e1-4', source: '1', target: '4' } ]; ``` ### Dark Mode This example demonstrates how to implement dark mode in Svelte Flow. You can customize the appearance of nodes, edges, and the background to create a cohesive dark theme that matches your application's design system. Example: examples/styling/dark-mode ##### App.svelte ```svelte ``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### nodes-and-edges.ts ```ts import { type Node, type Edge, Position } from '@xyflow/svelte'; const nodeDefaults = { sourcePosition: Position.Right, targetPosition: Position.Left }; export const initialNodes: Node[] = [ { id: 'A', position: { x: 0, y: 150 }, data: { label: 'A' }, ...nodeDefaults }, { id: 'B', position: { x: 250, y: 0 }, data: { label: 'B' }, ...nodeDefaults }, { id: 'C', position: { x: 250, y: 150 }, data: { label: 'C' }, ...nodeDefaults }, { id: 'D', position: { x: 250, y: 300 }, data: { label: 'D' }, ...nodeDefaults } ]; export const initialEdges: Edge[] = [ { id: 'a-b', source: 'A', target: 'B' }, { id: 'a-c', source: 'A', target: 'C' }, { id: 'a-d', source: 'A', target: 'D' } ]; ``` ### Tailwind CSS This example shows how to integrate Tailwind CSS with Svelte Flow. Learn how to use Tailwind's utility classes to style nodes, edges, and other flow components while maintaining a consistent design system across your application. Example: examples/styling/tailwind ##### App.svelte ```svelte ``` ##### CustomNode.svelte ```svelte
{data.emoji}
{data.name}
{data.job}
``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } @tailwind base; @tailwind components; @tailwind utilities; ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', type: 'custom', data: { name: 'Jane Doe', job: 'CEO', emoji: '😎' }, position: { x: 0, y: 50 } }, { id: '2', type: 'custom', data: { name: 'Tyler Weary', job: 'Designer', emoji: 'πŸ€“' }, position: { x: -200, y: 200 } }, { id: '3', type: 'custom', data: { name: 'Kristi Price', job: 'Developer', emoji: '🀩' }, position: { x: 200, y: 200 } } ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2' }, { id: 'e1-3', source: '1', target: '3' } ]; ``` ##### tailwind.config.js ```js /** @type {import('tailwindcss').Config} */ export default { // Needs to be important to override the default styles important: true, content: ['./src/**/*.{html,js,svelte,ts}'], theme: { extend: {} }, plugins: [] }; ``` ### Turbo Flow Every part of the Svelte Flow UI is customizable. As the name implies the look is taken from the beautiful [turbo.build](https://turbo.build/pack/docs/core-concepts#function-level-caching) website. You can find more information about custom styles in the [theming guide](/learn/customization/theming). (Svelte Flow uses Turborepo and we love it πŸ’œ) Example: examples/styling/turbo-flow ##### App.svelte ```svelte ``` ##### FunctionIcon.svelte ```svelte ``` ##### TurboEdge.svelte ```svelte ``` ##### TurboNode.svelte ```svelte
{#if data.icon}
{#if data.icon === 'function'} {:else if data.icon === 'file'} {/if}
{/if}
{data.title}
{#if data.subtitle}
{data.subtitle}
{/if}
``` ##### index.css ```css html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } .svelte-flow { --bg-color: rgb(17, 17, 17); --text-color: rgb(243, 244, 246); --node-border-radius: 10px; --node-box-shadow: 10px 0 15px rgba(42, 138, 246, 0.3), -10px 0 15px rgba(233, 42, 103, 0.3); background-color: var(--bg-color) !important; color: var(--text-color); } .svelte-flow__node-turbo { border-radius: var(--node-border-radius); display: flex; height: 70px; min-width: 150px; font-family: 'Fira Mono', Monospace; font-weight: 500; letter-spacing: -0.2px; box-shadow: var(--node-box-shadow); } .svelte-flow__node-turbo .wrapper { overflow: hidden; display: flex; padding: 2px; position: relative; border-radius: var(--node-border-radius); flex-grow: 1; } .gradient:before { content: ''; position: absolute; padding-bottom: calc(100% * 1.41421356237); width: calc(100% * 1.41421356237); background: conic-gradient( from -160deg at 50% 50%, #e92a67 0deg, #a853ba 120deg, #2a8af6 240deg, #e92a67 360deg ); left: 50%; top: 50%; transform: translate(-50%, -50%); border-radius: 100%; } .svelte-flow__node-turbo.selected .wrapper.gradient:before { content: ''; background: conic-gradient( from -160deg at 50% 50%, #e92a67 0deg, #a853ba 120deg, #2a8af6 240deg, rgba(42, 138, 246, 0) 360deg ); animation: spinner 4s linear infinite; transform: translate(-50%, -50%) rotate(0deg); z-index: -1; } @keyframes spinner { 100% { transform: translate(-50%, -50%) rotate(-360deg); } } .svelte-flow__node-turbo .inner { background: var(--bg-color); padding: 16px 20px; border-radius: var(--node-border-radius); display: flex; flex-direction: column; justify-content: center; flex-grow: 1; position: relative; } .svelte-flow__node-turbo .icon { margin-right: 8px; } .svelte-flow__node-turbo .body { display: flex; } .svelte-flow__node-turbo .title { font-size: 16px; margin-bottom: 2px; line-height: 1; } .svelte-flow__node-turbo .subtitle { font-size: 12px; color: #777; } .svelte-flow__node-turbo .cloud { border-radius: 100%; width: 30px; height: 30px; right: 0; position: absolute; top: 0; transform: translate(50%, -50%); display: flex; transform-origin: center center; padding: 2px; overflow: hidden; box-shadow: var(--node-box-shadow); z-index: 1; } .svelte-flow__node-turbo .cloud div { background-color: var(--bg-color); flex-grow: 1; border-radius: 100%; display: flex; justify-content: center; align-items: center; position: relative; } .svelte-flow__handle { opacity: 0; } .svelte-flow__handle.source { right: -10px; } .svelte-flow__handle.target { left: -10px; } .svelte-flow__node:focus { outline: none; } .svelte-flow__edge .svelte-flow__edge-path { stroke: url(#edge-gradient); stroke-width: 2; stroke-opacity: 0.75; } .svelte-flow__controls button { background-color: var(--bg-color); color: var(--text-color); border: 1px solid #95679e; border-bottom: none; } .svelte-flow__controls button:hover { background-color: rgb(37, 37, 37); } .svelte-flow__controls button:first-child { border-radius: 5px 5px 0 0; } .svelte-flow__controls button:last-child { border-bottom: 1px solid #95679e; border-radius: 0 0 5px 5px; } .svelte-flow__controls button path { fill: var(--text-color); } .svelte-flow__attribution { background: rgba(200, 200, 200, 0.2); } .svelte-flow__attribution a { color: #95679e; } ``` ##### nodes-and-edges.ts ```ts import type { Node, Edge } from '@xyflow/svelte'; export const initialNodes: Node[] = [ { id: '1', position: { x: 0, y: 0 }, data: { icon: 'function', title: 'readFile', subtitle: 'api.ts' }, type: 'turbo', }, { id: '2', position: { x: 250, y: 0 }, data: { icon: 'function', title: 'bundle', subtitle: 'apiContents' }, type: 'turbo', }, { id: '3', position: { x: 0, y: 250 }, data: { icon: 'function', title: 'readFile', subtitle: 'sdk.ts' }, type: 'turbo', }, { id: '4', position: { x: 250, y: 250 }, data: { icon: 'function', title: 'bundle', subtitle: 'sdkContents' }, type: 'turbo', }, { id: '5', position: { x: 500, y: 125 }, data: { icon: 'function', title: 'concat', subtitle: 'api, sdk' }, type: 'turbo', }, { id: '6', position: { x: 750, y: 125 }, data: { icon: 'file', title: 'fullBundle' }, type: 'turbo', }, ]; export const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', }, { id: 'e3-4', source: '3', target: '4', }, { id: 'e2-5', source: '2', target: '5', }, { id: 'e4-5', source: '4', target: '5', }, { id: 'e5-6', source: '5', target: '6', }, ]; ``` ### Eraser This example shows how to create an eraser tool that allows you to delete nodes and edges by wiping them out. It's made up of two parts: 1. The `Eraser` component that handles the erasing logic and rendering of the eraser trail. 2. The custom `ErasableNode` and `ErasableEdge` that reacts to the `toBeDeleted` flag. Determining if the trail intersects with a node is fairly straight forward - however detecting intersections between the trail and an edge is a bit more complex: We sample points along the edge through the [`getPointAtLength`](https://developer.mozilla.org/en-US/docs/Web/API/SVGGeometryElement/getPointAtLength) method of the SVG path element, construct a polyline that we can then use to detect intersections with the eraser trail. This is a trade-off between performance and accuracy - you can play around with the `sampleDistance` variable to see the effect it has on the eraser trail. Example: examples/whiteboard/eraser ##### App.svelte ```svelte {#if isEraserActive} {/if}
``` ##### ErasableEdge.svelte ```svelte ``` ##### ErasableNode.svelte ```svelte
{data.label}
``` ##### Eraser.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### utils.ts ```ts // Utility functions for geometric intersection detection // Type definitions for better type safety type Point = [number, number]; type Rectangle = { x: number; y: number; width: number; height: number }; // Check if two line segments intersect function lineSegmentsIntersect(p1: Point, p2: Point, p3: Point, p4: Point): boolean { const [x1, y1] = p1; const [x2, y2] = p2; const [x3, y3] = p3; const [x4, y4] = p4; const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); if (Math.abs(denom) < 1e-10) return false; // Lines are parallel const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom; const u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom; return t >= 0 && t <= 1 && u >= 0 && u <= 1; } // Check if a point is inside a rectangle function pointInRectangle(point: Point, rect: Rectangle): boolean { const [x, y] = point; return ( x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height ); } // Get the four edges of a rectangle as line segments function getRectangleEdges(rect: Rectangle): [Point, Point][] { const { x, y, width, height } = rect; return [ [ [x, y], [x + width, y], ], // top edge [ [x + width, y], [x + width, y + height], ], // right edge [ [x + width, y + height], [x, y + height], ], // bottom edge [ [x, y + height], [x, y], ], // left edge ]; } // Check if a polyline (series of connected line segments) intersects with a rectangle export function polylineIntersectsRectangle(points: Point[], rect: Rectangle): boolean { if (points.length < 2) return false; // Early return if any point is inside the rectangle for (const point of points) { if (pointInRectangle(point, rect)) { return true; } } // Check if any line segment intersects with rectangle edges const rectEdges = getRectangleEdges(rect); for (let i = 0; i < points.length - 1; i++) { const lineStart = points[i]; const lineEnd = points[i + 1]; for (const [edgeStart, edgeEnd] of rectEdges) { if (lineSegmentsIntersect(lineStart, lineEnd, edgeStart, edgeEnd)) { return true; } } } return false; } // Calculate distance between two points function distanceBetweenPoints(p1: Point, p2: Point): number { const [x1, y1] = p1; const [x2, y2] = p2; return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); } // Calculate the closest point on a line segment to a given point function closestPointOnSegment( point: Point, segmentStart: Point, segmentEnd: Point, ): Point { const [px, py] = point; const [x1, y1] = segmentStart; const [x2, y2] = segmentEnd; const dx = x2 - x1; const dy = y2 - y1; const lengthSquared = dx * dx + dy * dy; if (lengthSquared === 0) return segmentStart; // Segment is a point const t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / lengthSquared)); return [x1 + t * dx, y1 + t * dy]; } // Check if two paths intersect using a more efficient approach export function pathsIntersect( path1: Point[], path2: Point[], threshold: number = 1, ): boolean { if (path1.length < 2 || path2.length < 2) return false; // First, do the more precise line segment intersection check for (let i = 0; i < path1.length - 1; i++) { for (let j = 0; j < path2.length - 1; j++) { if (lineSegmentsIntersect(path1[i], path1[i + 1], path2[j], path2[j + 1])) { return true; } } } // If no exact intersection, check for proximity based on threshold if (threshold > 0) { for (let i = 0; i < path1.length - 1; i++) { const segment1Start = path1[i]; const segment1End = path1[i + 1]; for (let j = 0; j < path2.length - 1; j++) { const segment2Start = path2[j]; const segment2End = path2[j + 1]; // Check distance between segment endpoints and closest points const distances = [ distanceBetweenPoints( segment1Start, closestPointOnSegment(segment1Start, segment2Start, segment2End), ), distanceBetweenPoints( segment1End, closestPointOnSegment(segment1End, segment2Start, segment2End), ), distanceBetweenPoints( segment2Start, closestPointOnSegment(segment2Start, segment1Start, segment1End), ), distanceBetweenPoints( segment2End, closestPointOnSegment(segment2End, segment1Start, segment1End), ), ]; if (Math.min(...distances) <= threshold) { return true; } } } } return false; } // Simplified path sampling for cases where you need discrete points export function samplePathPoints(points: Point[], maxDistance: number = 5): Point[] { if (points.length < 2) return [...points]; const result: Point[] = [points[0]]; for (let i = 1; i < points.length; i++) { const prev = result[result.length - 1]; const current = points[i]; const distance = distanceBetweenPoints(prev, current); if (distance > maxDistance) { // Add intermediate points const numSegments = Math.ceil(distance / maxDistance); for (let j = 1; j < numSegments; j++) { const t = j / numSegments; const interpolated: Point = [ prev[0] + (current[0] - prev[0]) * t, prev[1] + (current[1] - prev[1]) * t, ]; result.push(interpolated); } } result.push(current); } return result; } ``` ### Lasso Selection This example demonstrates how to implement a lasso selection tool that allows users to select multiple nodes by drawing a freehand selection area. The component features: * **Lasso Drawing**: Draw freehand selection areas using pointer events * **Partial/Full Selection**: Toggle between selecting nodes partially or fully enclosed by the lasso * **Visual Feedback**: Real-time visual feedback while drawing the selection area * **Canvas Rendering**: Uses HTML5 Canvas for smooth drawing performance Example: examples/whiteboard/lasso-selection ##### App.svelte ```svelte {#if isLassoActive} {/if}
``` ##### Lasso.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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### index.css ```css @import url('./xy-theme.css'); html, body { margin: 0; font-family: sans-serif; } #app { width: 100vw; height: 100vh; } ``` ##### utils.ts ```ts import getStroke from "perfect-freehand"; export const pathOptions = { size: 7, thinning: 0.5, smoothing: 0.5, streamline: 0.5, easing: (t: number) => t, start: { taper: 0, easing: (t: number) => t, cap: true, }, end: { taper: 0.1, easing: (t: number) => t, cap: true, }, }; export function getSvgPathFromStroke(stroke: number[][]) { if (!stroke.length) return ""; const d = stroke.reduce( (acc, [x0, y0], i, arr) => { const [x1, y1] = arr[(i + 1) % arr.length]; acc.push(x0, y0, ",", (x0 + x1) / 2, (y0 + y1) / 2); return acc; }, ["M", ...stroke[0], "Q"], ); d.push("Z"); return d.join(" "); } export function pointsToPath(points: [number, number, number][], zoom = 1) { const stroke = getStroke(points, { ...pathOptions, size: pathOptions.size * zoom, }); return getSvgPathFromStroke(stroke); } ``` ### Rectangle In this example, you can draw rectangles on a whiteboard. Click and drag to create a rectangle, and it will be added to the flow. Example: examples/whiteboard/rectangle ##### App.svelte ```svelte
{#if isRectangleActive} {/if}
``` ##### RectangleNode.svelte ```svelte
{#each colorOptions as colorOption} {/each}
``` ##### RectangleTool.svelte ```svelte
{#if rect}
{/if}
``` ##### 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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### 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; } .svelte-flow__node { padding: 0; border: none; } ``` ## 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. * `width?: number` Sets a fixed width for the flow * `height?: number` Sets a fixed height for the flow * `nodes?: Node[]` An array of nodes to render in a flow. * `edges?: Edge[]` An array of edges to render in a flow. * `nodeTypes?: NodeTypes` Custom node types to be available in a flow. Svelte Flow matches a node's type to a component in the nodeTypes object. * `edgeTypes?: EdgeTypes` Custom edge types to be available in a flow. Svelte Flow matches an edge's type to a component in the edgeTypes object. * `connectionDragThreshold?: number` The threshold in pixels that the mouse must move before a connection line starts to drag. This is useful to prevent accidental connections when clicking on a handle. * `autoPanSpeed?: number` The speed at which the viewport pans while dragging a node or a selection box. * `autoPanOnNodeFocus?: boolean` When `true`, the viewport will pan when a node is focused. * `panOnScrollSpeed?: number` Controls how fast viewport should be panned on scroll. Use together with `panOnScroll` prop. * `colorMode?: ColorMode` Controls color scheme used for styling the flow * `colorModeSSR?: Omit` Fallback color mode for SSR if colorMode is set to 'system' * `proOptions?: ProOptions` By default, we render a small attribution in the corner of your flows that links back to the project. You are free to remove this attribution but we ask that you take a quick look at our removing attribution guide before doing so. * `ariaLabelConfig?: Partial` Configuration for customizable labels, descriptions, and UI text. Provided keys will override the corresponding defaults. Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings. * `...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` Ocassionally 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 insted 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[]` Enableing 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 * `x: number` The `x` position of the edge toolbar. * `y: number` The `y` position of the edge toolbar. * `isVisible?: boolean` If `true`, edge toolbar is visible even if edge is not selected. * `alignX?: "left" | "center" | "right"` Align the vertical toolbar position relative to the passed x position. * `alignY?: "center" | "top" | "bottom"` Align the horizontal toolbar position relative to the passed y position. * `selectEdgeOnClick?: boolean` * `...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 `
`. * `type: 'source' | 'target'` Type of the handle. * `position: Position` The position of the handle relative to the node. In a horizontal flow source handles are typically `Position.Right` and in a vertical flow they are typically `Position.Top`. * `isConnectable?: boolean` Should you be able to connect to/from this handle. * `isConnectableStart?: boolean` Dictates whether a connection can start from this handle. * `isConnectableEnd?: boolean` Dictates whether a connection can end on this handle. * `isValidConnection?: IsValidConnection` Called when a connection is dragged to this handle. You can use this callback to perform some custom validation logic based on the connection target and source, for example. Where possible, we recommend you move this logic to the `isValidConnection` prop on the main ReactFlow component for performance reasons. * `onconnect?: (connections: HandleConnection[]) => void` * `ondisconnect?: (connections: HandleConnection[]) => void` * `...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 `
`. * `bgColor?: string` Background color of minimap * `nodeColor?: string | GetMiniMapNodeAttribute` Color of nodes on the minimap * `nodeStrokeColor?: string | GetMiniMapNodeAttribute` Stroke color of nodes on the minimap * `nodeClass?: string | GetMiniMapNodeAttribute` Class applied to nodes on the minimap * `nodeBorderRadius?: number` Border radius of nodes on the minimap * `nodeStrokeWidth?: number` Stroke width of nodes on the minimap * `nodeComponent?: Component` A custom component to render the nodes in the minimap. This component must render an SVG element! * `maskColor?: string` Color of the mask representing viewport * `maskStrokeColor?: string` Stroke color of the mask representing viewport * `maskStrokeWidth?: number` Stroke width of the mask representing viewport * `position?: PanelPosition` Position of the minimap on the pane * `ariaLabel?: string | null` The aria-label applied to container * `width?: number` Width of minimap * `height?: number` Height of minimap * `pannable?: boolean` * `zoomable?: boolean` * `inversePan?: boolean` Invert the direction when panning the minimap viewport * `zoomStep?: number` Step size for zooming in/out * `...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; } .svelte-flow.dark { --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */ 0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */ 0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */ --xy-theme-color-focus: #535353; } /* 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: #d9d9d9; } .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__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: white; } .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 { border: none; border-radius: 0; width: 5px; height: 5px; } .svelte-flow__edge-label { background: #f7f9fb; } /* 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: inherit; } .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: inherit; 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; } ``` ##### 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 `
`. * `nodeId?: string | string[]` The id of the node, or array of ids the toolbar should be displayed at * `position?: Position` Position of the toolbar relative to the node * `align?: Align` Align the toolbar relative to the node * `offset?: number` Offset the toolbar from the node * `isVisible?: boolean` If true, node toolbar is visible even if node is not selected * `...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 `
`. * `position?: PanelPosition` Set position of the panel * `...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 `
`. * `target: "front" | "back"` * `...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: Pick | 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?: ViewportHelperFunctionOptions) => Promise` Zooms viewport in by 1.2. * `zoomOut: (options?: ViewportHelperFunctionOptions) => 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: PromiseWithResolvers | null` * `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?: SetCenterOptions) => 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`