# Svelte Flow Documentation
What is Svelte Flow?
Svelte Flow is a library that allows you to create interactive, node-based user
interfaces: flowcharts, diagrams, visual programming tools, and workflows inside
your svelte applications. It supports theming, custom nodes and edges, a library
of shadcn UI components, and offers a large collection of examples for rapid development.
Developers can leverage the Svelte Flow Pro platform for advanced features like
real-time collaboration, complex layouts, and enhanced performance, making it
suitable for both simple and large-scale, production-ready visual applications.
## Learn
### Quick Start
This page will take you from zero to a working Svelte Flow app in a few minutes. From
there, you can take a deeper look at what Svelte Flow is all about, check out the
examples, or dive into the API docs. If you want to get up and running as soon as
possible, you're in the right place!
#### Installation
To install Svelte Flow in your existing Svelte project, run the following command:
```bash copy npm2yarn
npm install @xyflow/svelte
```
#### Templates
If you want to get started right away, you can use a template.
##### Svelte Flow Vite template
We have a ready-to-use
[Vite template](https://github.com/xyflow/vite-svelte-flow-template) that you can use to
get up and running in no time.
```bash copy npm2yarn
npx degit xyflow/vite-svelte-flow-template app-name
```
##### SvelteKit template
Alternatively, you can spin up a new [Svelte](https://svelte.dev/) with
[SvelteKit](https://svelte.dev/docs/kit/introduction) and [Vite](https://vite.dev/)
templates.
```bash copy npm2yarn
npx sv create my-svelte-flow-app
```
Next, `cd` into your project directory and install the Svelte Flow package:
```bash copy npm2yarn
npm install @xyflow/svelte
```
#### Usage
The `@xyflow/svelte` package exports the `` component, which is the
entrypoint for your flow. Importing the default styles and defining a handful of nodes and
edges are all we need to get started!
There are a few things to pay attention to here:
* You must import the Svelte Flow stylesheet.
* `` inherits the size of its parent. Wrap it in an
element with dimensions.
* Use
[`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) instead of deeply
reactive state for the `nodes` and `edges` for [performance
reasons](https://github.com/sveltejs/svelte/issues/11851).
```svelte
```
#### Result
Et voila. You've already created your first interactive flow!
Example: learn/quickstart
##### App.svelte
```svelte
```
##### index.css
```css
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Next steps
### Server Side Rendering
### Server side rendering, server side generation
This is an advanced use case and assumes you are already familiar with SvelteFlow. If you're new to SvelteFlow, check out our [getting started guide](/learn/concepts/terms-and-definitions).
In this guide, you'll learn how to configure SvelteFlow for server-side rendering, enabling you to:
* Generate static HTML diagrams for documentation
* Render SvelteFlow diagrams in non-JavaScript environments
* Create dynamic Open Graph images for social media sharing
(For client-side image generation, check out our [download image example](/examples/misc/download-image).)
##### Why Server-Side Rendering is Complex
To understand why server-side rendering in Svelte Flow requires special configuration, let's look at what SvelteFlow typically handles on the client side:
1. **Node Dimension Calculation**
* Nodes can contain any content, so their dimensions are determined by the browser's layout engine
* This dynamic sizing isn't available during server-side rendering
2. **Handle Position Detection**
* Edge connections require precise handle positions
* These positions are calculated based on CSS layout, which isn't available on the server
3. **Container Size Adaptation**
* SvelteFlow adapts to its container's dimensions
* Server-side rendering needs explicit dimensions
##### Node Dimensions
The most crucial aspect of server-side rendering is specifying node dimensions. On the client, SvelteFlow automatically measures nodes and stores dimensions in `measured.width` and `measured.height`. For server-side rendering, you must provide these dimensions explicitly using either:
Node Dimension Options:
1. `width` and `height`: Static dimensions that won't change
2. `initialWidth` and `initialHeight`: Dynamic dimensions that may change after
client-side hydration
```svelte
```
##### Handle Positions
To render edges on the server, you need to provide handle positions explicitly. On the client, SvelteFlow calculates these positions automatically, but for server-side rendering, you must specify them using the `handles` property:
```svelte
```
##### Using `fitView` with Server-Side Rendering
If you know your container's dimensions, you can use `fitView` during server-side rendering by providing the container's width and height:
```svelte
```
##### Generating Static HTML
To create static HTML output, you can use Svelte's server-side rendering capabilities. This generates an HTML string that you can use for static files or HTTP responses:
```svelte
```
### Usage with TypeScript
Svelte Flow is written in TypeScript because we value the additional safety barrier it provides.
We export all the types you need for correctly typing data structures and functions you pass to the Svelte Flow component. We also provide a way to extend the types of nodes and edges.
#### Basic Usage
Let's start with the essential types needed for a basic implementation. While TypeScript can infer some types automatically, we'll define them explicitly for clarity.
```svelte
```
##### Custom Nodes
When working with [custom nodes](/learn/customization/custom-nodes), you can extend the base `Node` type to include your custom data. There are two main approaches:
1. For **multiple custom nodes**, specify a custom `Node` type as a generic to `NodeProps`:
```svelte
A special number: {data.number}
```
β οΈ When defining node data separately, you must use `type` (interfaces won't work):
```ts
type NumberNodeData = { number: number };
type NumberNodeType = Node;
```
2. For **a single custom node** that renders different content based on the node type, use a union type:
```svelte
{#if data.type === 'number'}
A special number: {data.number}
{:else}
A special text: {data.text}
{/if}
```
##### Custom Edges
Similar to custom nodes, you can extend the base `Edge` type for [custom edges](/learn/customization/custom-edges):
```svelte
```
#### Advanced Usage
In complex applications, you'll likely have multiple custom nodes and edges with different data structures. When using built-in functions and hooks, you'll need to properly [narrow down](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) the types to prevent runtime errors.
##### `Node` and `Edge` Type Unions
Many functions, callbacks, and hooks (including the SvelteFlow component) expect `NodeType` or `EdgeType` generics. These are unions of all your custom node and edge types. As long as you've properly typed your data objects, you can use their exported types.
If you're using any built-in nodes ('input', 'output', 'default') or edges ('straight',
'step', 'smoothstep', 'bezier'), include the `BuiltInNode` and `BuiltInEdge` types from
`@xyflow/svelte` in your union type.
```svelte
```
##### Hooks
You can use these type unions to properly type the return values of hooks:
```svelte
```
##### Type Guards
TypeScript provides several ways to implement [type guards](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#typeof-type-guards). One common approach is to create type guard functions like `isNumberNode` or `isTextNode` to filter specific nodes from a list:
```ts
function isNumberNode(node: NodeType): node is NumberNodeType {
return node.type === 'number';
}
// numberNodes is now correctly typed as NumberNodeType[]
let numberNodes = $derived(nodes.filter(isNumberNode));
```
### Building a Flow
In the following pages we will introduce you to the core concepts of Svelte Flow and
explain how to create a basic interactive flow. A flow consists of
[nodes](/api-reference/types/node), [edges](/api-reference/types/edge) and the viewport.
If you haven't reviewed our [Key Concepts](/learn/concepts/terms-and-definitions) yet,
we recommend doing that first.
To follow along with this guide you will need to have a Svelte project set up and install
the `@xyflow/svelte` package:
```bash copy npm2yarn
npm install @xyflow/svelte
```
#### Creating the flow
Let's start by creating an empty flow with viewport
[``](/api-reference/components/controls) and a dotted
[``](/api-reference/components/background).
### Add imports
First, import the Svelte Flow Component and its required styles into your
project. We'll also import the `Background` component for visual enhancement,
and the `Controls` component for the viewport controls.
```svelte
```
##### Render SvelteFlow
Next, render the main component inside an element with defined dimensions and place the
[``](/api-reference/components/background) and [``](/api-reference/components/controls) components inside `SvelteFlow`.
Content inside `SvelteFlow` stays fixed on top of the viewport. The `Background`
component transforms its pattern to match viewport movement.
```svelte
```
##### Empty flow
That's it! You have created your first empty flow π
If everything is set up correctly, you should see a blank canvas like this:
Example: learn/building-a-flow-1
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Adding nodes
Now that the flow is set up, let's add some nodes - each node represents an element in
your diagram with a specific position and content.
### Create node objects
Outside of your Svelte component, in the `
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
We have several built-in nodes that you can explore in the
[node](/api-reference/types/node) reference. However, once you start building your own
application, you will want to use [custom nodes](/learn/customization/custom-nodes).
#### Adding edges
Now that we have two nodes, letβs connect them with an edge.
### Create edge objects
To create an edge, we define an array of [edge](/api-reference/types/edge) objects.
Each edge object needs to have an `id`, a `source` (where the edge begins), and a `target`
(where it ends). In this example, we use the `id` values of the two nodes we created so
far (`n1` and `n2`) to define the edge:
We use the [`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) rune to make the array reactive.
```js
let edges = $state.raw([
{ id: 'n1-n2', source: 'n1', target: 'n2' },
]);
```
##### Add edges to the flow
As with nodes, we [bind](https://svelte.dev/docs/svelte/$bindable) the edges to the
`SvelteFlow` component.
```svelte "bind:edges"
```
Your flow should now look like this:
Example: learn/building-a-flow-3
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### Edge label and type
By default, Svelte Flow allows you to add text labels to edges that will
float in the middle of the edge via the `label` property.
Also, many types of edges are built in, such as `smoothstep` and `step`.
We can change the type of the edge by setting the `type` property.
```js {6-7}
let edges = $state.raw([
{
id: 'n1-n2',
source: 'n1',
target: 'n2',
type: 'smoothstep',
label: 'connects with',
},
]);
```
##### Done! π
Congratulations! You have completed a basic flow with nodes and edges! π
#### Full code example π
You took your first steps in Svelte Flow! The completed flow will look like this:
Example: learn/building-a-flow-4
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
/* This is just here to better match the design of svelteflow.dev */
.svelte-flow {
--xy-background-color: #f7f9fb;
--xy-edge-label-background-color-default: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
### Built-In Components
Svelte Flow comes with several additional components that you can plug into your flow. All
you have to do is import and add them as children to the `SvelteFlow` component.
```svelte filename="BuiltInComponents.svelte"
My Flow
```
#### MiniMap
The [`MiniMap`](/api-reference/components/mini-map) provides a bird's-eye view of your
flowgraph, making navigation easier, especially for larger flows. You can customize the
appearance of nodes in the minimap by providing a `nodeColor` function.
Example: learn/minimap
##### App.svelte
```svelte
{
switch (node.type) {
case 'input':
return '#6ede87';
case 'output':
return '#6865A5';
default:
return '#ff0072';
}
}}
zoomable
pannable
/>
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
```
##### index.html
```html
SvelteFlow MiniMap Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Controls
Svelte Flow comes with a set of customizable
[`Controls`](/api-reference/components/controls) for the viewport. You can zoom in and
out, fit the viewport and toggle if the user can move, select and edit the nodes.
Example: learn/controls
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
```
##### index.html
```html
SvelteFlow Controls Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Background
The [`Background`](/api-reference/components/background) component adds a visual grid
pattern to your flowgraph, helping users maintain orientation. You can choose from
different pattern variants, or if you need more advanced customization, you can explore
the
[source code](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Background/Background.svelte)
to implement your own pattern.
Example: learn/background
##### App.svelte
```svelte
variant:
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
SvelteFlow Background Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Panel
The [`Panel`](/api-reference/components/panel) component allows you to add fixed overlays
to your flowgraph, perfect for titles, controls, or any other UI elements that should
remain stationary.
Example: learn/panel
##### App.svelte
```svelte
top-lefttop-centertop-rightbottom-leftbottom-centerbottom-rightcenter-leftcenter-right
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
.svelte-flow__panel {
padding: 5px 10px;
background: white;
box-shadow:
rgba(0, 0, 0, 0.1) 0px 1px 3px 0px,
rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;
}
```
##### index.html
```html
SvelteFlow Panel Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
### Overview
At its core, Svelte Flow is about creating interactive flowgraphs - a collection of nodes connected by edges. While this might sound simple in theory, Svelte Flow provides the foundation for building complex interactive diagrams:
* An infinite, interactive canvas for your flowgraph
* The ability to render and connect nodes with edges
* **Everything is built using standard Svelte components**
To help you understand the terminology we use throughout the documentation, take a look at the example flow below.
Example: learn/basic-terms
##### AnnotationNode.svelte
```svelte
On the bottom left you see the Controls and the bottom right the{' '}
MiniMap. This is also just a node π₯³
```
##### NodeWrapper.svelte
```svelte
{@render children?.()}
```
##### ResizerNode.svelte
```svelte
{data.label}
```
##### TextNode.svelte
```svelte
{#each ['width', 'height'] as attr}
{/each}
{#if dimensions === null}
no node connected
{/if}
```
##### ToolbarNode.svelte
```svelte
{#each emojis as emoji (emoji)}
{/each}
{selectedEmoji}
{data.label}
```
##### xy-theme.css
```css
/* xyflow theme files. Delete these to start from our base */
.svelte-flow {
--xy-background-color: #f7f9fb;
/* Custom Variables */
--xy-theme-selected: #ff4000;
--xy-theme-hover: #c5c5c5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #e8e8e8;
/* Built-in Variables see https://svelteflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #ededed;
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d,
0px 0.51px 1.01px 0px #0000001a;
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #aaaaaa;
--xy-edge-label-color-default: #505050;
--xy-resize-background-color-default: #9e86ed;
--xy-node-background-color-default: #ffffff;
--xy-theme-panel-bg: #ffffff;
--xy-theme-panel-text: #111827;
--xy-theme-resize-handle-bg: #ffffff;
--xy-theme-focus-border: #d9d9d9;
--xy-theme-muted-bg: #f3f4f6;
--xy-theme-subtle-border: #d1d5db;
}
.svelte-flow.dark {
--xy-background-color: #101012;
--xy-node-background-color-default: #1a1b1e;
--xy-node-border-default: 1px solid #303238;
--xy-handle-background-color-default: #141519;
--xy-handle-border-color-default: #5a5d66;
--xy-edge-label-color-default: #e6e7ea;
--xy-theme-hover: #7a7d86;
--xy-theme-edge-hover: #f3f4f6;
--xy-theme-panel-bg: #141519;
--xy-theme-panel-text: #f3f4f6;
--xy-theme-resize-handle-bg: #141519;
--xy-theme-focus-border: #5f6470;
--xy-theme-muted-bg: #24262c;
--xy-theme-subtle-border: #3f434c;
--xy-node-boxshadow-default:
0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3),
0px 1px 2px 0px rgba(0, 0, 0, 0.35);
--xy-theme-color-focus: #4b4e57;
--color-background: #1a1b1e;
--color-hover-bg: #24262c;
--color-disabled: #8b9099;
}
.svelte-flow {
background-color: var(--xy-background-color);
}
/* Customizing Default Theming */
.svelte-flow__node {
box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 12px;
flex-direction: column;
border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default));
}
.svelte-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: var(--xy-theme-focus-border);
}
.svelte-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node.selectable:hover,
.svelte-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.svelte-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9e86ed;
}
.svelte-flow.dark .svelte-flow__node-group {
background-color: rgba(123, 107, 148, 0.22);
border-color: #7b6b94;
}
.svelte-flow__edge.selectable:hover .svelte-flow__edge-path,
.svelte-flow__edge.selectable.selected .svelte-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.svelte-flow__handle {
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:focus,
.svelte-flow__handle.connectingfrom,
.svelte-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.svelte-flow__node:has(.svelte-flow__resize-control.line) {
border-radius: 0;
}
.svelte-flow__resize-control.handle {
background-color: var(--xy-theme-resize-handle-bg);
border: none;
border-radius: 0;
width: 5px;
height: 5px;
}
.svelte-flow__edge-label {
background: var(--xy-background-color);
color: var(--xy-edge-label-color-default);
}
/*
Custom Example CSS - This CSS is to improve the example experience.
You can remove it if you want to use the default styles.
New Theme Classes:
.xy-theme__button - Styles for buttons.
.xy-theme__input - Styles for text inputs.
.xy-theme__checkbox - Styles for checkboxes.
.xy-theme__select - Styles for dropdown selects.
.xy-theme__label - Styles for labels.
Use these classes to apply consistent theming across your components.
*/
:root {
--color-primary: #ff4000;
--color-background: #fefefe;
--color-hover-bg: #f6f6f6;
--color-disabled: #76797e;
}
/* Custom Button Styling */
.xy-theme__button-group {
display: flex;
align-items: center;
.xy-theme__button:first-child {
border-radius: 100px 0 0 100px;
}
.xy-theme__button:last-child {
border-radius: 0 100px 100px 0;
margin: 0;
}
}
.xy-theme__button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
height: 2.5rem;
padding: 0 1rem;
border-radius: 100px;
border: 1px solid var(--color-primary);
background-color: var(--color-background);
color: var(--color-primary);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.xy-theme__button.active:hover,
.xy-theme__button.active:active {
background-color: var(--color-primary);
opacity: 0.9;
}
.xy-theme__button:hover {
background-color: var(--xy-controls-button-background-color-hover-default);
}
.xy-theme__button:active {
background-color: var(--color-hover-bg);
}
.xy-theme__button:disabled {
color: var(--color-disabled);
opacity: 0.8;
cursor: not-allowed;
border: 1px solid var(--color-disabled);
}
.xy-theme__button > span {
margin-right: 0.2rem;
}
/* Add gap between adjacent buttons */
.xy-theme__button + .xy-theme__button {
margin-left: 0.3rem;
}
/* Example Input Styling */
.xy-theme__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 7px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
}
.xy-theme__input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Specific Checkbox Styling */
.xy-theme__checkbox {
appearance: none;
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 7px;
border: 2px solid var(--color-primary);
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
cursor: pointer;
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.xy-theme__checkbox:checked {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.xy-theme__checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Dropdown Styling */
.xy-theme__select {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 50px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
margin-right: 0.5rem;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
.xy-theme__label {
margin-top: 10px;
margin-bottom: 3px;
display: inline-block;
color: var(--xy-theme-panel-text);
}
```
##### index.css
```css
@import url('./xy-theme.css');
html,
body {
margin: 0;
font-family: sans-serif;
box-sizing: border-box;
}
#app {
width: 100vw;
height: 100vh;
}
/* Circle Node */
.svelte-flow__node-circle {
border-radius: 50%;
height: 100px;
width: 100px;
font-family: monospace;
text-align: center;
}
/* Text Input Node */
.svelte-flow__node-textinput {
width: 150px;
font-family: monospace;
text-align: left;
}
.text-input-node__input {
width: 100%;
box-sizing: border-box;
margin: 5px 0px;
border-radius: 3px;
border: var(--xy-node-border-default);
}
.text-input-node__input::-webkit-outer-spin-button,
input[type='number']::-webkit-inner-spin-button {
-webkit-appearance: none;
}
.text-input-node__input {
-moz-appearance: textfield;
appearance: textfield;
}
/* Annotation Node */
.svelte-flow__node-annotation {
font-size: 16px;
width: 250px;
color: #683bfa;
position: absolute;
box-shadow: none;
font-family: monospace;
text-align: left;
background-color: transparent;
border: none;
pointer-events: none;
}
.svelte-flow__node-annotation .annotation-content {
padding: 10px;
display: flex;
}
.svelte-flow__node-annotation .annotation-level {
margin-right: 4px;
}
.svelte-flow__node-annotation .annotation-arrow {
position: absolute;
font-size: 24px;
}
/* Toolbar Node */
.svelte-flow__node-toolbar {
background-color: #000000;
border-radius: 16px;
overflow: hidden;
}
.svelte-flow__node-toolbar button {
cursor: pointer;
background: inherit;
border: none;
padding: 5px 7px;
margin: 3px;
border-radius: 50%;
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-toolbar button:hover {
background: #4d4d4d;
}
/* Resizer Node */
.resizer-node__handles {
display: flex;
position: absolute;
bottom: 0;
width: 100%;
justify-content: space-evenly;
left: 0;
}
.resizer-node__handle {
position: relative;
left: 0;
transform: none;
}
/* Button Edge */
.button-edge__label {
position: absolute;
pointer-events: all;
transform-origin: center;
background: transparent;
}
.button-edge__button {
width: 30px;
height: 30px;
border: 5px solid #f7f9fb;
color: var(--xy-edge-node-color-default);
background-color: #f3f3f4;
cursor: pointer;
border-radius: 50%;
font-size: 12px;
padding-top: 0px;
}
.button-edge__button:hover {
background-color: var(--xy-theme-hover);
color: #ffffff;
}
/* Custom Handles */
.svelte-flow__handle.custom-handle {
background-color: var(--xy-handle-border-color-default);
border-radius: 1px;
width: 8px;
height: 4px;
border: none;
min-width: 2px;
min-height: 2px;
}
.svelte-flow__handle.custom-handle:hover,
.svelte-flow__handle.custom-handle.connectionindicator:focus,
.svelte-flow__handle.custom-handle.connectingfrom,
.svelte-flow__handle.custom-handle.connectingto {
background-color: var(--xy-theme-edge-hover);
}
.svelte-flow__handle-bottom.custom-handle {
bottom: -5px;
transform: none;
}
.svelte-flow__handle-top.custom-handle {
top: -5px;
transform: none;
}
.svelte-flow__handle-left.custom-handle {
height: 8px;
width: 4px;
left: -3px;
}
/* Minimap */
.svelte-flow__minimap .group {
fill-opacity: 0.4;
}
.svelte-flow__minimap .resizer,
.svelte-flow__minimap .tools,
.svelte-flow__minimap .circle,
.svelte-flow__minimap .textinput {
fill: rgb(208, 192, 247);
}
.svelte-flow__minimap .circle {
rx: 100%;
ry: 100%;
}
.svelte-flow__minimap .annotation {
display: none;
}
.viewport-logger {
font-family: monospace;
color: var(--xy-node-color-default);
padding: 5px;
border-radius: 3px;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### nodes-and-edges.ts
```ts
import { MarkerType, type Node, type Edge } from '@xyflow/svelte';
import { type BuiltInNode } from '@xyflow/svelte';
import { type AnnotationNode } from './AnnotationNode.svelte';
import { type CircleNode } from './CircleNode.svelte';
export type AppNode = BuiltInNode | AnnotationNode | CircleNode;
export const initialNodes: Node[] = [
{
id: 'annotation-1',
type: 'annotation',
draggable: false,
selectable: false,
data: {
label: 'this is a "node"',
arrowStyle: 'right: 0; bottom: 0; transform: translate(-30px,10px) rotate(-70deg);',
},
position: { x: -80, y: -50 },
},
{
id: '1-1',
type: 'input',
data: {
label: 'node label',
},
position: { x: 150, y: 0 },
},
{
id: 'annotation-2',
type: 'annotation',
draggable: false,
selectable: false,
data: {
label: 'this is a "handle"',
arrowStyle:
'left: 0; bottom: 0; transform: translate(0px, -25px) rotate(140deg) scale(-1, 1);',
},
position: { x: 230, y: 30 },
},
{
id: 'annotation-3',
type: 'annotation',
draggable: false,
selectable: false,
data: {
level: 2,
label: 'this is an "edge"',
arrowStyle:
'right: 0; bottom: 0; transform: translate(-10px, -25px) rotate(190deg);',
},
position: { x: 20, y: 120 },
},
{
id: '1-2',
type: 'default',
data: {
label: 'node label',
},
position: { x: 350, y: 200 },
},
{
id: 'annotation-4',
type: 'annotation',
draggable: false,
selectable: false,
data: {
label: 'try dragging the handle',
arrowStyle:
'left: 0; bottom: 0; transform: translate(-15px, -25px) rotate(140deg) scale(-1, 1);',
},
position: { x: 450, y: 220 },
},
];
export const initialEdges: Edge[] = [
{
id: 'e1-2',
source: '1-1',
target: '1-2',
label: 'edge label',
type: 'smoothstep',
},
{
id: 'e2-2',
source: '1-2',
target: '2-2',
type: 'smoothstep',
markerEnd: {
type: MarkerType.ArrowClosed,
},
},
];
```
##### Nodes
Svelte Flow has a few default node types out of the box, but customization is where the
magic of Svelte Flow truly happens. You can design your nodes to work exactly the way you
need them toβwhether thatβs embedding interactive form elements, displaying dynamic data
visualizations, or even incorporating multiple connection handles. Svelte Flow lays the
foundation, and your imagination does the rest.
We have a guide on creating your own [Custom Nodes](/learn/customization/custom-nodes) and
you can find all the options for customizing your nodes in the
[Node](/api-reference/types/node) reference.
##### Handles
A handle (also known as a βportβ in other libraries) is the attachment point where an edge
connects to a node. By default, they appear as grey circles on the top, bottom, left, or
right sides of a node. But they are just `div` elements, and can be positioned and styled
any way youβd like. When creating a custom node, you can include as many handles as
needed. For more information, refer to the [Handle](/learn/customization/handles) page.
##### Edges
Edges connect nodes. Every edge needs a target and a source node. Svelte Flow comes with
four built-in [edge types](/examples/edges/edge-types): `default` (bezier), `smoothstep`,
`step`, and `straight`. An edge is an SVG path that can be styled with CSS and is
completely customizable. If you are using multiple handles, you can reference them
individually to create multiple connections for a node.
Like custom nodes, you can also customize edges. Things that people do with custom edges
include:
* Adding buttons to remove edges
* Custom routing behavior
* Complex styling or interactions that cannot be solved with just one SVG path
For more information, refer to the [Edges](/learn/customization/custom-edges) page.
##### Selection
You can select an edge or a node by clicking on it. If you want to select multiple
nodes/edges via clicking, you can hold the `Meta/Control` key and click on multiple
elements to select them. If you want to change the keyboard key for multiselection to
something else, you can use the
[`multiSelectionKey`](/api-reference/svelte-flow#multiselectionkey) prop.
You can also select multiple edges/nodes by holding down `Shift` and dragging the mouse to
make a selection box. When you release the mouse, any node or edge that falls within the
selection box is selected. If you want to change the keyboard key for this behavior, you
can use the [`selectionKey`](/api-reference/svelte-flow#selectionkey) prop.
Selected nodes and edges are elevated (assigned a higher `zIndex` than other elements), so
that they are shown on top of all the other elements.
For default edges and nodes, selection is shown by a darker stroke/border than usual. If
you are using a custom node/edge, you can use the `selected` prop to customize selection
appearance inside your custom component.
##### Connection line
Svelte Flow has built-in functionality that allows you to click and drag from one handle to
another to create a new edge. While dragging, the placeholder edge is referred to as a
connection line. The connection line comes with the same four built-in types as edges and
is customizable. You can find the props for configuring the connection line in the
[connection props](/api-reference/svelte-flow#connection-line-props) reference.
##### Viewport
All of Svelte Flow is contained within the viewport. Each node has an x- and y-coordinate,
which indicates its position within the viewport. The viewport has x, y, and zoom values.
When you drag the pane, you change the x and y coordinates. When you zoom in or out, you
alter the zoom level. You can read more about the viewport in the [Viewport](/learn/concepts/the-viewport) page.
### Panning and Zooming
The default pan and zoom behavior of Svelte Flow is inspired by
[slippy maps](https://wiki.openstreetmap.org/wiki/Slippy_map). You pan by dragging your
pointer and zoom by scrolling. You can customize this behavior easily with the
[interaction](/api-reference/svelte-flow#interaction-props) and
[keyboard](/api-reference/svelte-flow#keyboard-props) props on the ``
component.
#### Viewport configurations
Here we will list and explain some configurations that other tools use.
##### Default viewport controls
As mentioned above, the `` default controls are as follows:
* `pan:` pointer drag
* `zoom:` pinch or scroll
* `select:` shift + pointer drag
Example: learn/zoom-pan
##### App.svelte
```svelte
```
##### xy-theme.css
```css
/* xyflow theme files. Delete these to start from our base */
.svelte-flow {
--xy-background-color: #f7f9fb;
/* Custom Variables */
--xy-theme-selected: #ff4000;
--xy-theme-hover: #c5c5c5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #e8e8e8;
/* Built-in Variables see https://svelteflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #ededed;
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d,
0px 0.51px 1.01px 0px #0000001a;
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #aaaaaa;
--xy-edge-label-color-default: #505050;
--xy-resize-background-color-default: #9e86ed;
--xy-node-background-color-default: #ffffff;
--xy-theme-panel-bg: #ffffff;
--xy-theme-panel-text: #111827;
--xy-theme-resize-handle-bg: #ffffff;
--xy-theme-focus-border: #d9d9d9;
--xy-theme-muted-bg: #f3f4f6;
--xy-theme-subtle-border: #d1d5db;
}
.svelte-flow.dark {
--xy-background-color: #101012;
--xy-node-background-color-default: #1a1b1e;
--xy-node-border-default: 1px solid #303238;
--xy-handle-background-color-default: #141519;
--xy-handle-border-color-default: #5a5d66;
--xy-edge-label-color-default: #e6e7ea;
--xy-theme-hover: #7a7d86;
--xy-theme-edge-hover: #f3f4f6;
--xy-theme-panel-bg: #141519;
--xy-theme-panel-text: #f3f4f6;
--xy-theme-resize-handle-bg: #141519;
--xy-theme-focus-border: #5f6470;
--xy-theme-muted-bg: #24262c;
--xy-theme-subtle-border: #3f434c;
--xy-node-boxshadow-default:
0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3),
0px 1px 2px 0px rgba(0, 0, 0, 0.35);
--xy-theme-color-focus: #4b4e57;
--color-background: #1a1b1e;
--color-hover-bg: #24262c;
--color-disabled: #8b9099;
}
.svelte-flow {
background-color: var(--xy-background-color);
}
/* Customizing Default Theming */
.svelte-flow__node {
box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 12px;
flex-direction: column;
border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default));
}
.svelte-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: var(--xy-theme-focus-border);
}
.svelte-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node.selectable:hover,
.svelte-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.svelte-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9e86ed;
}
.svelte-flow.dark .svelte-flow__node-group {
background-color: rgba(123, 107, 148, 0.22);
border-color: #7b6b94;
}
.svelte-flow__edge.selectable:hover .svelte-flow__edge-path,
.svelte-flow__edge.selectable.selected .svelte-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.svelte-flow__handle {
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:focus,
.svelte-flow__handle.connectingfrom,
.svelte-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.svelte-flow__node:has(.svelte-flow__resize-control.line) {
border-radius: 0;
}
.svelte-flow__resize-control.handle {
background-color: var(--xy-theme-resize-handle-bg);
border: none;
border-radius: 0;
width: 5px;
height: 5px;
}
.svelte-flow__edge-label {
background: var(--xy-background-color);
color: var(--xy-edge-label-color-default);
}
/*
Custom Example CSS - This CSS is to improve the example experience.
You can remove it if you want to use the default styles.
New Theme Classes:
.xy-theme__button - Styles for buttons.
.xy-theme__input - Styles for text inputs.
.xy-theme__checkbox - Styles for checkboxes.
.xy-theme__select - Styles for dropdown selects.
.xy-theme__label - Styles for labels.
Use these classes to apply consistent theming across your components.
*/
:root {
--color-primary: #ff4000;
--color-background: #fefefe;
--color-hover-bg: #f6f6f6;
--color-disabled: #76797e;
}
/* Custom Button Styling */
.xy-theme__button-group {
display: flex;
align-items: center;
.xy-theme__button:first-child {
border-radius: 100px 0 0 100px;
}
.xy-theme__button:last-child {
border-radius: 0 100px 100px 0;
margin: 0;
}
}
.xy-theme__button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
height: 2.5rem;
padding: 0 1rem;
border-radius: 100px;
border: 1px solid var(--color-primary);
background-color: var(--color-background);
color: var(--color-primary);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.xy-theme__button.active:hover,
.xy-theme__button.active:active {
background-color: var(--color-primary);
opacity: 0.9;
}
.xy-theme__button:hover {
background-color: var(--xy-controls-button-background-color-hover-default);
}
.xy-theme__button:active {
background-color: var(--color-hover-bg);
}
.xy-theme__button:disabled {
color: var(--color-disabled);
opacity: 0.8;
cursor: not-allowed;
border: 1px solid var(--color-disabled);
}
.xy-theme__button > span {
margin-right: 0.2rem;
}
/* Add gap between adjacent buttons */
.xy-theme__button + .xy-theme__button {
margin-left: 0.3rem;
}
/* Example Input Styling */
.xy-theme__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 7px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
}
.xy-theme__input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Specific Checkbox Styling */
.xy-theme__checkbox {
appearance: none;
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 7px;
border: 2px solid var(--color-primary);
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
cursor: pointer;
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.xy-theme__checkbox:checked {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.xy-theme__checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Dropdown Styling */
.xy-theme__select {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 50px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
margin-right: 0.5rem;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
.xy-theme__label {
margin-top: 10px;
margin-bottom: 3px;
display: inline-block;
color: var(--xy-theme-panel-text);
}
```
##### index.css
```css
@import url('./xy-theme.css');
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### Design tool viewport controls
If you prefer figma/sketch/design tool controls you can set
[`panOnScroll`](/api-reference/svelte-flow#panonscroll) and
[`selectionOnDrag`](/api-reference/svelte-flow#selectionondrag) to `true` and
[`panOnDrag`](/api-reference/svelte-flow#panondrag) to `false`:
* `pan:` scroll, middle / right mouse drag, space + pointer drag
* `zoom:` pinch or cmd + scroll
* `select:` pointer drag
Example: learn/zoom-pan-2
##### App.svelte
```svelte
```
##### xy-theme.css
```css
/* xyflow theme files. Delete these to start from our base */
.svelte-flow {
--xy-background-color: #f7f9fb;
/* Custom Variables */
--xy-theme-selected: #ff4000;
--xy-theme-hover: #c5c5c5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #e8e8e8;
/* Built-in Variables see https://svelteflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #ededed;
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d,
0px 0.51px 1.01px 0px #0000001a;
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #aaaaaa;
--xy-edge-label-color-default: #505050;
--xy-resize-background-color-default: #9e86ed;
--xy-node-background-color-default: #ffffff;
--xy-theme-panel-bg: #ffffff;
--xy-theme-panel-text: #111827;
--xy-theme-resize-handle-bg: #ffffff;
--xy-theme-focus-border: #d9d9d9;
--xy-theme-muted-bg: #f3f4f6;
--xy-theme-subtle-border: #d1d5db;
}
.svelte-flow.dark {
--xy-background-color: #101012;
--xy-node-background-color-default: #1a1b1e;
--xy-node-border-default: 1px solid #303238;
--xy-handle-background-color-default: #141519;
--xy-handle-border-color-default: #5a5d66;
--xy-edge-label-color-default: #e6e7ea;
--xy-theme-hover: #7a7d86;
--xy-theme-edge-hover: #f3f4f6;
--xy-theme-panel-bg: #141519;
--xy-theme-panel-text: #f3f4f6;
--xy-theme-resize-handle-bg: #141519;
--xy-theme-focus-border: #5f6470;
--xy-theme-muted-bg: #24262c;
--xy-theme-subtle-border: #3f434c;
--xy-node-boxshadow-default:
0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3),
0px 1px 2px 0px rgba(0, 0, 0, 0.35);
--xy-theme-color-focus: #4b4e57;
--color-background: #1a1b1e;
--color-hover-bg: #24262c;
--color-disabled: #8b9099;
}
.svelte-flow {
background-color: var(--xy-background-color);
}
/* Customizing Default Theming */
.svelte-flow__node {
box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 12px;
flex-direction: column;
border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default));
}
.svelte-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: var(--xy-theme-focus-border);
}
.svelte-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node.selectable:hover,
.svelte-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.svelte-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9e86ed;
}
.svelte-flow.dark .svelte-flow__node-group {
background-color: rgba(123, 107, 148, 0.22);
border-color: #7b6b94;
}
.svelte-flow__edge.selectable:hover .svelte-flow__edge-path,
.svelte-flow__edge.selectable.selected .svelte-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.svelte-flow__handle {
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:focus,
.svelte-flow__handle.connectingfrom,
.svelte-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.svelte-flow__node:has(.svelte-flow__resize-control.line) {
border-radius: 0;
}
.svelte-flow__resize-control.handle {
background-color: var(--xy-theme-resize-handle-bg);
border: none;
border-radius: 0;
width: 5px;
height: 5px;
}
.svelte-flow__edge-label {
background: var(--xy-background-color);
color: var(--xy-edge-label-color-default);
}
/*
Custom Example CSS - This CSS is to improve the example experience.
You can remove it if you want to use the default styles.
New Theme Classes:
.xy-theme__button - Styles for buttons.
.xy-theme__input - Styles for text inputs.
.xy-theme__checkbox - Styles for checkboxes.
.xy-theme__select - Styles for dropdown selects.
.xy-theme__label - Styles for labels.
Use these classes to apply consistent theming across your components.
*/
:root {
--color-primary: #ff4000;
--color-background: #fefefe;
--color-hover-bg: #f6f6f6;
--color-disabled: #76797e;
}
/* Custom Button Styling */
.xy-theme__button-group {
display: flex;
align-items: center;
.xy-theme__button:first-child {
border-radius: 100px 0 0 100px;
}
.xy-theme__button:last-child {
border-radius: 0 100px 100px 0;
margin: 0;
}
}
.xy-theme__button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
height: 2.5rem;
padding: 0 1rem;
border-radius: 100px;
border: 1px solid var(--color-primary);
background-color: var(--color-background);
color: var(--color-primary);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.xy-theme__button.active:hover,
.xy-theme__button.active:active {
background-color: var(--color-primary);
opacity: 0.9;
}
.xy-theme__button:hover {
background-color: var(--xy-controls-button-background-color-hover-default);
}
.xy-theme__button:active {
background-color: var(--color-hover-bg);
}
.xy-theme__button:disabled {
color: var(--color-disabled);
opacity: 0.8;
cursor: not-allowed;
border: 1px solid var(--color-disabled);
}
.xy-theme__button > span {
margin-right: 0.2rem;
}
/* Add gap between adjacent buttons */
.xy-theme__button + .xy-theme__button {
margin-left: 0.3rem;
}
/* Example Input Styling */
.xy-theme__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 7px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
}
.xy-theme__input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Specific Checkbox Styling */
.xy-theme__checkbox {
appearance: none;
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 7px;
border: 2px solid var(--color-primary);
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
cursor: pointer;
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.xy-theme__checkbox:checked {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.xy-theme__checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Dropdown Styling */
.xy-theme__select {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 50px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
margin-right: 0.5rem;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
.xy-theme__label {
margin-top: 10px;
margin-bottom: 3px;
display: inline-block;
color: var(--xy-theme-panel-text);
}
```
##### index.css
```css
@import url('./xy-theme.css');
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
In this example we also set `selectionMode="partial"` to be able to add nodes to a
selection that are only partially selected.
### Custom Edges
Like [custom nodes](/learn/customization/custom-nodes), parts of a custom edge in Svelte
Flow are just Svelte components: that means you can render anything you want along an
edge! This guide shows you how to implement a custom edge with some additional controls.
For a comprehensive reference of props available for custom edges, see the
[`EdgeProps`](/api-reference/types/edge-props) documentation.
#### A basic custom edge
An edge isn't much use to us if it doesn't render a path between two connected nodes.
These paths are always SVG-based and are typically rendered using the
[``](/api-reference/components/base-edge) component. To calculate the actual
SVG path to render, Svelte Flow comes with some handy utility functions:
* [`getBezierPath`](/api-reference/utils/get-bezier-path)
* [`getSmoothStepPath`](/api-reference/utils/get-smooth-step-path)
* [`getStraightPath`](/api-reference/utils/get-straight-path)
To kick start our custom edge, we'll just render a straight path between the source and
target.
```svelte filename="CustomEdge.svelte"
```
All the props passed to your custom edge component can be found in the API reference
under the [`EdgeProps`](/api-reference/types/edge-props) type.
This gives us a straight edge that behaves the same as the default `"straight"`
[edge type](/api-reference/types/edge#default-edge-types). To use it, we also need to
update the [`edgeTypes`](/api-reference/svelte-flow#edge-types) prop on the
`` component.
It's important to define the `edgeTypes` object *outside of the component* or to use
Svelte's `$derived` to prevent unnecessary re-renders. Svelte Flow will show a warning in
the console if you forget to do this.
```svelte filename="App.svelte"
```
After defining the `edgeTypes` object, we can use our new custom edge by setting the
`type` field of an edge to `"custom-edge"`.
Example: learn/custom-edge
##### App.svelte
```svelte
```
##### CustomEdge.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
margin: 0;
font-family: sans-serif;
width: 100%;
height: 100%;
}
/* This is just here to better match the design of svelteflow.dev */
.svelte-flow {
--xy-background-color: #f7f9fb;
--xy-edge-label-background-color-default: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Making Custom SVG Edge Paths
As discussed previously, if you want to make a custom edge in Svelte Flow, you have to use
either of the four path creation functions discussed above (e.g
[`getBezierPath`](/api-reference/utils/get-bezier-path)). However if you want to make some
other path shape like a Sinusoidal edge or some other edge type then you will have to make
the edge path yourself.
The edge path we get from functions like
[`getBezierPath`](/api-reference/utils/get-bezier-path) is just a path string which we
pass into the `path` prop of the `` component. It contains the necessary
information needed in order to draw that path, like where it should start from, where it
should curve, where it should end, etc. A simple straight path string between two points
`(x1, y1)` to `(x2, y2)` would look like:
```svelte
M x1 y1 L x2 y2
```
An SVG path is a concatenated list of commands like `M`, `L`, `Q`, etc, along with their
values. Some of these commands are listed below, along with their supported values.
* `M x1 y1` is the Move To command which moves the current point to the x1, y1 coordinate.
* `L x1 y1` is the Line To command which draws a line from the current point to x1, y1
coordinate.
* `Q x1 y1 x2 y2` is the Quadratic Bezier Curve command which draws a bezier curve from
the current point to the x2, y2 coordinate. x1, y1 is the control point of the curve
which determines the curviness of the curve.
Whenever we want to start a path for our custom edge, we use the `M` command to move our
current point to `sourceX, sourceY` which we get as props in the custom edge component.
Then based on the shape we want, we will use other commands like `L`(to make lines),
`Q`(to make curves) and then finally end our path at `targetX, targetY` which we get as
props in the custom edge component.
If you want to learn more about SVG paths, you can check out
[SVG-Path-Editor](https://yqnn.github.io/svg-path-editor/). You can paste any SVG path
there and analyze individual path commands via an intuitive UI.
Here is an example with two types of custom edge paths, a Step edge and a Sinusoidal edge.
You should look at the Step edge first to get your hands dirty with custom SVG paths since
it's simple, and then look at how the Sinusoidal edge is made. After going through this
example, you will have the necessary knowledge to make custom SVG paths for your custom
edges.
Example: learn/custom-edge-path
##### App.svelte
```svelte
```
##### SineEdge.svelte
```svelte
```
##### StepEdge.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
margin: 0;
font-family: sans-serif;
width: 100%;
height: 100%;
}
/* This is just here to better match the design of svelteflow.dev */
.svelte-flow {
--xy-background-color: #f7f9fb;
--xy-edge-label-background-color-default: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
### Custom Nodes
A powerful feature of Svelte Flow is the ability to create custom nodes. This gives you the flexibility to render anything you want within your nodes. We generally recommend creating your own custom nodes rather than relying on built-in ones. With custom nodes, you can add as many source and target handles as you likeβor even embed form inputs, charts, and other interactive elements.
In this section, we'll walk through creating a custom node featuring an input field that updates text elsewhere in your application. For further examples, we recommend checking out our [Custom Node Example](/examples/nodes/custom-node).
#### Implementing a Custom Node
To create a custom node, all you need to do is create a Svelte component. Svelte Flow will automatically wrap it in an interactive container that injects essential props like the node's id, position, and data, and provides functionality for selection, dragging, and connecting handles. For a full reference on all available custom node props, take a look at the [Node Props](/api-reference/types/node-props).
Let's dive into an example by creating a custom node called `TextUpdaterNode`. For this, we've added a controlled input field with a oninput handler. We simply use the 'text' property from the node's data for the input and we update the node's data via the [`updateNodeData`](/api-reference/hooks/use-svelte-flow#update-node-data) function, that can be accessed through the [`useSvelteFlow`](/api-reference/hooks/use-svelte-flow) hook.
```svelte filename="TextUpdaterNode.svelte"
```
#### Adding the Node Type
Now we need to communicate the new custom node to Svelte Flow. You can add custom nodes by passing the [`nodeTypes`](/api-reference/svelte-flow#node-types) prop.
```svelte filename="App.svelte"
```
After defining your new node type, you can refer to it by using the `type` node option:
```js
let nodes = $state.raw([
{
id: 'node-1',
type: 'textUpdater',
position: { x: 0, y: 0 },
data: { text: 'some text' },
},
]);
```
After putting it all together and adding some basic styles we get a custom node that prints text to the console:
Example: learn/custom-node
##### App.svelte
```svelte
```
##### TextUpdaterNode.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
.text-updater-node {
height: 50px;
padding: 5px;
background: var(--xy-node-background-color-default);
}
.text-updater-node label {
display: block;
color: var(--xy-node-color-default);
font-size: 12px;
}
.text-updater-node input {
color: var(--xy-node-color-default);
background: var(--xy-node-background-color-default);
/* border: 1px solid var(--xy-node-border-color-default); */
border-radius: 3px;
padding: 5px;
font-size: 12px;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
To enable your custom node to connect with other nodes, check out the
[Handles](/learn/customization/handles) page to learn how to add source and target
handles.
### Edge Labels
One of the more common uses for custom edges is rendering some controls or info along an
edge's path. In Svelte Flow we call that an *edge label* and unlike the edge path, edge
labels can be any Svelte component!
#### Adding an edge label
Because Svelte Flow's edges are mounted inside an SVG component, we need to escape its
context to render a custom edge label. For this, we have a handy
[``](/api-reference/components/edge-label) component. Aside from a couple of
extras, like inheriting the edges z-index, it functions as a portal that mounts the child
components in the viewport div.
Let's add a button to our custom edge that can be used to delete the edge it's attached
to:
```svelte filename="CustomEdge.svelte"
```
To make sure our edge labels are interactive and not just for presentation, it is
important to add the `nodrag` and `nopan` classes to the label to stop mouse events from
controlling the canvas.
Here's an interactive example with our updated custom edge. Clicking the delete button
will remove that edge from the flow. Creating a new edge will use the custom node.
Example: learn/custom-edge-2
##### App.svelte
```svelte
```
##### CustomEdge.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body,
#app {
margin: 0;
font-family: sans-serif;
width: 100%;
height: 100%;
}
/* This is just here to better match the design of svelteflow.dev */
.svelte-flow {
--xy-background-color: #f7f9fb;
--xy-edge-label-background-color-default: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
### Handles
Handles are the connection points on [nodes](/learn/concepts/terms-and-definitions#nodes)
in Svelte Flow. Our built-in nodes include one source and one target handle, but you can
customize your nodes with as many different handles as you need.
#### Creating a node with handles
To create a [custom node](/learn/customization/custom-nodes) with handles, you can use the
[``](/api-reference/components/handle) component provided by Svelte Flow. This
component allows you to define source and target handles for your custom nodes. Here's an
example of how to implement a custom node with two handles:
```svelte {7-8}
Custom Node Content
```
#### Using multiple handles
If you want to use multiple source or target handles in your custom node, you need to
specify each handle with a unique `id`. This allows Svelte Flow to differentiate between
the handles when connecting edges.
```svelte /id="a"/ /id="b"/
```
To connect an edge to a specific handle of a node, use the properties `sourceHandle` (for
the edge's starting point) and `targetHandle` (for the edge's ending point). By defining
`sourceHandle` or `targetHandle` with the appropriate handle `id`, you instruct Svelte Flow
to attach the edge to that specific handle, ensuring that connections are made where you
intend.
```js "sourceHandle: 'a'" "sourceHandle: 'b'"
const initialEdges = [
{ id: 'n1-n2', source: 'n1', sourceHandle: 'a', target: 'n2' },
{ id: 'n1-n3', source: 'n1', sourceHandle: 'b', target: 'n3' },
];
```
In this case, the source node is `n1` for both handles but the handle `id`s are different.
One comes from handle id `a` and the other one from `b`. Both edges also have different
target nodes:
Example: learn/custom-node-2
##### App.svelte
```svelte
```
##### TextUpdaterNode.svelte
```svelte
Custom Node
```
##### xy-theme.css
```css
/* xyflow theme files. Delete these to start from our base */
.svelte-flow {
--xy-background-color: #f7f9fb;
/* Custom Variables */
--xy-theme-selected: #ff4000;
--xy-theme-hover: #c5c5c5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #e8e8e8;
/* Built-in Variables see https://svelteflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #ededed;
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d,
0px 0.51px 1.01px 0px #0000001a;
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #aaaaaa;
--xy-edge-label-color-default: #505050;
--xy-resize-background-color-default: #9e86ed;
--xy-node-background-color-default: #ffffff;
--xy-theme-panel-bg: #ffffff;
--xy-theme-panel-text: #111827;
--xy-theme-resize-handle-bg: #ffffff;
--xy-theme-focus-border: #d9d9d9;
--xy-theme-muted-bg: #f3f4f6;
--xy-theme-subtle-border: #d1d5db;
}
.svelte-flow.dark {
--xy-background-color: #101012;
--xy-node-background-color-default: #1a1b1e;
--xy-node-border-default: 1px solid #303238;
--xy-handle-background-color-default: #141519;
--xy-handle-border-color-default: #5a5d66;
--xy-edge-label-color-default: #e6e7ea;
--xy-theme-hover: #7a7d86;
--xy-theme-edge-hover: #f3f4f6;
--xy-theme-panel-bg: #141519;
--xy-theme-panel-text: #f3f4f6;
--xy-theme-resize-handle-bg: #141519;
--xy-theme-focus-border: #5f6470;
--xy-theme-muted-bg: #24262c;
--xy-theme-subtle-border: #3f434c;
--xy-node-boxshadow-default:
0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3),
0px 1px 2px 0px rgba(0, 0, 0, 0.35);
--xy-theme-color-focus: #4b4e57;
--color-background: #1a1b1e;
--color-hover-bg: #24262c;
--color-disabled: #8b9099;
}
.svelte-flow {
background-color: var(--xy-background-color);
}
/* Customizing Default Theming */
.svelte-flow__node {
box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 12px;
flex-direction: column;
border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default));
}
.svelte-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: var(--xy-theme-focus-border);
}
.svelte-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node.selectable:hover,
.svelte-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.svelte-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9e86ed;
}
.svelte-flow.dark .svelte-flow__node-group {
background-color: rgba(123, 107, 148, 0.22);
border-color: #7b6b94;
}
.svelte-flow__edge.selectable:hover .svelte-flow__edge-path,
.svelte-flow__edge.selectable.selected .svelte-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.svelte-flow__handle {
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:focus,
.svelte-flow__handle.connectingfrom,
.svelte-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.svelte-flow__node:has(.svelte-flow__resize-control.line) {
border-radius: 0;
}
.svelte-flow__resize-control.handle {
background-color: var(--xy-theme-resize-handle-bg);
border: none;
border-radius: 0;
width: 5px;
height: 5px;
}
.svelte-flow__edge-label {
background: var(--xy-background-color);
color: var(--xy-edge-label-color-default);
}
/*
Custom Example CSS - This CSS is to improve the example experience.
You can remove it if you want to use the default styles.
New Theme Classes:
.xy-theme__button - Styles for buttons.
.xy-theme__input - Styles for text inputs.
.xy-theme__checkbox - Styles for checkboxes.
.xy-theme__select - Styles for dropdown selects.
.xy-theme__label - Styles for labels.
Use these classes to apply consistent theming across your components.
*/
:root {
--color-primary: #ff4000;
--color-background: #fefefe;
--color-hover-bg: #f6f6f6;
--color-disabled: #76797e;
}
/* Custom Button Styling */
.xy-theme__button-group {
display: flex;
align-items: center;
.xy-theme__button:first-child {
border-radius: 100px 0 0 100px;
}
.xy-theme__button:last-child {
border-radius: 0 100px 100px 0;
margin: 0;
}
}
.xy-theme__button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
height: 2.5rem;
padding: 0 1rem;
border-radius: 100px;
border: 1px solid var(--color-primary);
background-color: var(--color-background);
color: var(--color-primary);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.xy-theme__button.active:hover,
.xy-theme__button.active:active {
background-color: var(--color-primary);
opacity: 0.9;
}
.xy-theme__button:hover {
background-color: var(--xy-controls-button-background-color-hover-default);
}
.xy-theme__button:active {
background-color: var(--color-hover-bg);
}
.xy-theme__button:disabled {
color: var(--color-disabled);
opacity: 0.8;
cursor: not-allowed;
border: 1px solid var(--color-disabled);
}
.xy-theme__button > span {
margin-right: 0.2rem;
}
/* Add gap between adjacent buttons */
.xy-theme__button + .xy-theme__button {
margin-left: 0.3rem;
}
/* Example Input Styling */
.xy-theme__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 7px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
}
.xy-theme__input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Specific Checkbox Styling */
.xy-theme__checkbox {
appearance: none;
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 7px;
border: 2px solid var(--color-primary);
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
cursor: pointer;
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.xy-theme__checkbox:checked {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.xy-theme__checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Dropdown Styling */
.xy-theme__select {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 50px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
margin-right: 0.5rem;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
.xy-theme__label {
margin-top: 10px;
margin-bottom: 3px;
display: inline-block;
color: var(--xy-theme-panel-text);
}
```
##### index.css
```css
@import url('./xy-theme.css');
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
By default Svelte Flow positions a handle in the center of the specified side. If you want
to display multiple handles on a side, you can adjust the position via inline styles or
overwrite the default CSS and position it on your own.
#### Custom handles
You can use any custom component as a Handle by wrapping it with the
[``](/api-reference/components/handle) component. Follow these steps to
integrate your custom component:
1. Wrap your custom component with [``](/api-reference/components/handle)
component.
2. Hide the built-in [``](/api-reference/components/handle) appearance by
setting `border` and `background` to `none`.
3. Set the `width` and `height` of [``](/api-reference/components/handle) to
match your custom component.
4. Style the child component with `pointer-events: none`.
5. Then, reposition the child custom component using CSS position properties like
`top, left` if necessary to position it perfectly inside the
[``](/api-reference/components/handle) component.
This example shows a custom icon used as a Handle:
```svelte
β
Custom Node
```
#### Typeless handles
If you want to create a handle that does not have a specific type (source or target), you
can set [connectionMode](/api-reference/svelte-flow#connectionmode) to `Loose` in the
`` component. This allows the handle to be used for both incoming and
outgoing connections.
#### Dynamic handles
If you are programmatically changing the position or number of handles in your custom
node, you need to update the node internals with the
[`useUpdateNodeInternals`](/api-reference/hooks/use-update-node-internals) hook.
#### Custom handle styles
Since the handle is a div, you can use CSS to style it or pass a style prop to customize a
Handle. You can see this in the
[Add Node On Edge Drop](/examples/nodes/add-node-on-edge-drop) example.
##### Styling handles when connecting
The handle receives the additional class names `connectingfrom` and `connectingto` when the connection line is
above the handle and `valid` if the connection is valid. You can find an example which
uses these classes [here](/examples/interaction/validation).
##### Hiding handles
If you need to hide a handle for some reason, you must use `visibility: hidden` or
`opacity: 0` instead of `display: none`. This is important because Svelte Flow needs to
calculate the dimensions of the handle to work properly and using `display: none` will
report a width and height of `0`!
### Theming
Svelte Flow comes with minimal default styles and was designed to be fully customizable.
Many of our users fully transform the look and feel of Svelte Flow to match their own
brand or design system. This guide will introduce you to the different ways you can
customize Svelte Flow's appearance.
#### Default styles
Svelte Flow's default styles are enough to get going with the built-in nodes. They provide
some sensible defaults for styles like padding, border radius, and animated edges. You can
see what they look like below:
```js
import '@xyflow/svelte/dist/style.css';
```
Example: learn/theming-default
##### App.svelte
```svelte
```
##### index.css
```css
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
You'll typically load these default styles by importing them in your `+layout.svelte`,
`App.svelte`, or another entry point.
Without dipping into [custom nodes](/learn/customization/custom-nodes) and
[edges](/learn/customization/custom-edges), there are several ways you can style Svelte
Flow's basic look:
* Passing inline styles through `style` props
* Overriding the built-in classes with custom CSS
* Overriding the CSS variables Svelte Flow uses
* Using scoped styles in your Svelte components for custom nodes and edges
#### Base styles
If you just want to load the very basic styles that are necessary for Svelte Flow to work,
you can import the base styles instead:
```js
import '@xyflow/svelte/dist/base.css';
```
These base styles are **required** for Svelte Flow to function correctly. If you don't
expected!
Example: learn/theming-base
##### App.svelte
```svelte
```
##### index.css
```css
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
#### Customization
##### Built in dark and light mode
You can choose one of the built-in color modes by using the `colorMode` prop (`'dark'`,
`'light'`, or `'system'`) as seen in the [dark mode example](/examples/styling/dark-mode).
```svelte
```
When you use the `colorMode` prop, Svelte Flow adds a class to the root element
(`.svelte-flow`) that you can use to style your flow based on the color mode:
```css
.dark .svelte-flow__node {
background: #777;
color: white;
}
.light .svelte-flow__node {
background: white;
color: #111;
}
```
##### Customizing with `style` props
The easiest way to start customizing the look and feel of your flows is to use the `style`
prop on `SvelteFlow` and other components to inline your own CSS.
```svelte
```
##### 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. For an example of how to use these CSS variables, check out our
[Feature Overview](/examples/overview) example.
These variables are mostly self-explanatory. Below is a table of all the variables you
might want to tweak and their default values for reference:
These variables are used to define the *defaults* for the various elements of Svelte Flow.
This means they can still be overridden by inline styles or custom classes on a
per-element basis. If you want to override a variable, you can do so by adding:
```css
.svelte-flow {
--xy-node-background-color-default: #ff5050;
}
```
Be aware that these variables are defined under `.svelte-flow` and under `:root`.
##### Overriding built-in classes
Some consider heavy use of inline styles to be an anti-pattern. In that case, you can
override the built-in classes that Svelte Flow uses with your own CSS. There are many
classes attached to all sorts of elements in Svelte Flow, but the ones you'll likely want
to override are listed below:
Be careful if you go poking around the source code looking for other classes to
override. Some classes are used internally and are required in order for the library to
be functional. If you replace them you may end up with unexpected bugs or errors!
##### Scoped styles for custom nodes and edges
Custom nodes and edges are Svelte components. You can use
[`scoped` styles](https://svelte.dev/docs/svelte/scoped-styles) (or global CSS with
`@apply`) in those components to keep styling isolated from the rest of your app.
#### Third-party solutions
You can choose to opt out of Svelte Flow's default styling altogether and use a
third-party styling solution instead. If you want to do this, you must make sure you still
##### TailwindCSS
Custom nodes and edges are just Svelte components, and you can use any styling solution
you'd like to style them. For example, you might want to use
[Tailwind](https://tailwindcss.com/) to style your nodes:
```svelte
{data.emoji}
{data.name}
{data.job}
```
If you want to overwrite default styles, make sure to import Tailwind's entry point after
Svelte Flow's base styles.
```js
import '@xyflow/svelte/dist/style.css';
import 'tailwind.css';
```
For a complete example of using Tailwind with Svelte Flow, check out the
[tailwind example](/examples/styling/tailwind)!
### Utility Classes
Svelte Flow provides several built-in utility CSS classes to help you fine-tune how
interactions work within your custom elements.
#### `nodrag`
Adding the class `nodrag` to an element ensures that interacting with it doesn't trigger a
drag. This is particularly useful for elements like buttons or inputs that should not
initiate a drag operation when clicked.
Nodes have a `drag` class name in place by default. However, this class name can affect
the behaviour of the event listeners inside your custom nodes. To prevent unexpected
behaviours, add a `nodrag` class name to elements with an event listener. This prevents
the default drag behavior as well as the default node selection behavior when elements
with this class are clicked.
```svelte
```
#### `nopan`
If an element in the canvas does not stop mouse events from propagating, clicking and
dragging that element will pan the viewport. Adding the `nopan` class prevents this
behavior. You can change the class name with the
[`noPanClass`](/api-reference/svelte-flow#style-props) prop on ``.
```svelte
fixed content...
```
#### `nowheel`
If your custom element contains scrollable content, you can apply the `nowheel` class.
This disables the canvas' default pan behavior when you scroll inside your custom node,
ensuring that only the content scrolls instead of moving the entire canvas. You can
change the class name with the [`noWheelClass`](/api-reference/svelte-flow#style-props)
prop on ``.
```svelte
Scrollable content...
```
Applying these utility classes helps you control interaction on a granular level. You can
customize these class names on `` via the
[style props](/api-reference/svelte-flow#style-props).
When creating your own custom nodes, you will also need to remember to style them!
Unlike the built-in nodes, custom nodes have no default styles, so feel free to use any
styling method you prefer, such as [Tailwind CSS](/examples/styling/tailwind).
### Overview
We regularly get asked how to handle layouting in Svelte Flow. While we could build
some basic layouting into Svelte Flow, we believe that **you know your app's
requirements best** and with so many options out there we think it's better you
choose the best right tool for the job (not to mention it'd be a whole bunch of
work for us).
To start let's put together a simple example flow that we can use as a base for testing
out the different layouting options.
Example: learn/layouting-flow-1-empty
##### App.svelte
```svelte
```
##### LayoutFlow\.svelte
```svelte
```
##### xy-theme.css
```css
/* xyflow theme files. Delete these to start from our base */
.svelte-flow {
--xy-background-color: #f7f9fb;
/* Custom Variables */
--xy-theme-selected: #ff4000;
--xy-theme-hover: #c5c5c5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #e8e8e8;
/* Built-in Variables see https://svelteflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #ededed;
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d,
0px 0.51px 1.01px 0px #0000001a;
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #aaaaaa;
--xy-edge-label-color-default: #505050;
--xy-resize-background-color-default: #9e86ed;
--xy-node-background-color-default: #ffffff;
--xy-theme-panel-bg: #ffffff;
--xy-theme-panel-text: #111827;
--xy-theme-resize-handle-bg: #ffffff;
--xy-theme-focus-border: #d9d9d9;
--xy-theme-muted-bg: #f3f4f6;
--xy-theme-subtle-border: #d1d5db;
}
.svelte-flow.dark {
--xy-background-color: #101012;
--xy-node-background-color-default: #1a1b1e;
--xy-node-border-default: 1px solid #303238;
--xy-handle-background-color-default: #141519;
--xy-handle-border-color-default: #5a5d66;
--xy-edge-label-color-default: #e6e7ea;
--xy-theme-hover: #7a7d86;
--xy-theme-edge-hover: #f3f4f6;
--xy-theme-panel-bg: #141519;
--xy-theme-panel-text: #f3f4f6;
--xy-theme-resize-handle-bg: #141519;
--xy-theme-focus-border: #5f6470;
--xy-theme-muted-bg: #24262c;
--xy-theme-subtle-border: #3f434c;
--xy-node-boxshadow-default:
0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3),
0px 1px 2px 0px rgba(0, 0, 0, 0.35);
--xy-theme-color-focus: #4b4e57;
--color-background: #1a1b1e;
--color-hover-bg: #24262c;
--color-disabled: #8b9099;
}
.svelte-flow {
background-color: var(--xy-background-color);
}
/* Customizing Default Theming */
.svelte-flow__node {
box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 12px;
flex-direction: column;
border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default));
}
.svelte-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: var(--xy-theme-focus-border);
}
.svelte-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node.selectable:hover,
.svelte-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.svelte-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9e86ed;
}
.svelte-flow.dark .svelte-flow__node-group {
background-color: rgba(123, 107, 148, 0.22);
border-color: #7b6b94;
}
.svelte-flow__edge.selectable:hover .svelte-flow__edge-path,
.svelte-flow__edge.selectable.selected .svelte-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.svelte-flow__handle {
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:focus,
.svelte-flow__handle.connectingfrom,
.svelte-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.svelte-flow__node:has(.svelte-flow__resize-control.line) {
border-radius: 0;
}
.svelte-flow__resize-control.handle {
background-color: var(--xy-theme-resize-handle-bg);
border: none;
border-radius: 0;
width: 5px;
height: 5px;
}
.svelte-flow__edge-label {
background: var(--xy-background-color);
color: var(--xy-edge-label-color-default);
}
/*
Custom Example CSS - This CSS is to improve the example experience.
You can remove it if you want to use the default styles.
New Theme Classes:
.xy-theme__button - Styles for buttons.
.xy-theme__input - Styles for text inputs.
.xy-theme__checkbox - Styles for checkboxes.
.xy-theme__select - Styles for dropdown selects.
.xy-theme__label - Styles for labels.
Use these classes to apply consistent theming across your components.
*/
:root {
--color-primary: #ff4000;
--color-background: #fefefe;
--color-hover-bg: #f6f6f6;
--color-disabled: #76797e;
}
/* Custom Button Styling */
.xy-theme__button-group {
display: flex;
align-items: center;
.xy-theme__button:first-child {
border-radius: 100px 0 0 100px;
}
.xy-theme__button:last-child {
border-radius: 0 100px 100px 0;
margin: 0;
}
}
.xy-theme__button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
height: 2.5rem;
padding: 0 1rem;
border-radius: 100px;
border: 1px solid var(--color-primary);
background-color: var(--color-background);
color: var(--color-primary);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.xy-theme__button.active:hover,
.xy-theme__button.active:active {
background-color: var(--color-primary);
opacity: 0.9;
}
.xy-theme__button:hover {
background-color: var(--xy-controls-button-background-color-hover-default);
}
.xy-theme__button:active {
background-color: var(--color-hover-bg);
}
.xy-theme__button:disabled {
color: var(--color-disabled);
opacity: 0.8;
cursor: not-allowed;
border: 1px solid var(--color-disabled);
}
.xy-theme__button > span {
margin-right: 0.2rem;
}
/* Add gap between adjacent buttons */
.xy-theme__button + .xy-theme__button {
margin-left: 0.3rem;
}
/* Example Input Styling */
.xy-theme__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 7px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
}
.xy-theme__input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Specific Checkbox Styling */
.xy-theme__checkbox {
appearance: none;
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 7px;
border: 2px solid var(--color-primary);
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
cursor: pointer;
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.xy-theme__checkbox:checked {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.xy-theme__checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Dropdown Styling */
.xy-theme__select {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 50px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
margin-right: 0.5rem;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
.xy-theme__label {
margin-top: 10px;
margin-bottom: 3px;
display: inline-block;
color: var(--xy-theme-panel-text);
}
```
##### index.css
```css
@import url('./xy-theme.css');
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### nodes-edges.js
```js
export const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'input' },
position: { x: 0, y: 0 },
},
{
id: '2',
data: { label: 'node 2' },
position: { x: 0, y: 100 },
},
{
id: '2a',
data: { label: 'node 2a' },
position: { x: 0, y: 200 },
},
{
id: '2b',
data: { label: 'node 2b' },
position: { x: 0, y: 300 },
},
{
id: '2c',
data: { label: 'node 2c' },
position: { x: 0, y: 400 },
},
{
id: '2d',
data: { label: 'node 2d' },
position: { x: 0, y: 500 },
},
{
id: '3',
data: { label: 'node 3' },
position: { x: 200, y: 100 },
},
];
export const initialEdges = [
{ id: 'e12', source: '1', target: '2', animated: true },
{ id: 'e13', source: '1', target: '3', animated: true },
{ id: 'e22a', source: '2', target: '2a', animated: true },
{ id: 'e22b', source: '2', target: '2b', animated: true },
{ id: 'e22c', source: '2', target: '2c', animated: true },
{ id: 'e2c2d', source: '2c', target: '2d', animated: true },
];
```
Each of the examples that follow will be built on this empty flow. Where possible we've
tried to keep the examples confined to just one `index.js` file so it's easy for you to
compare how they're set up.
#### Layouting Nodes
For layouting nodes, there are a few third-party libraries that we think are worth
checking out:
ΒΉ Dagre currently has an [open issue](https://github.com/dagrejs/dagre/issues/238)
that prevents it from laying out sub-flows correctly if any nodes in the sub-flow
are connected to nodes outside the sub-flow.
We've loosely ordered these options from simplest to most complex, where dagre
is largely a drop-in solution and elkjs is a full-blown highly configurable
layouting engine. Below, we'll take a look at a brief example of how each of these
libraries can be used with Svelte Flow. For dagre and elkjs specifically, we have
some separate examples you can refer back to [here](/examples/layout/dagre)
and [here](/examples/layout/elkjs).
##### Dagre
* Repo:
* Docs:
Dagre is a simple library for layouting directed graphs. It has minimal configuration
options and a focus on speed over choosing the most optimal layout. If you need
to organize your flows into a tree, *we highly recommend dagre*.
Example: learn/layouting-flow-2-dagre
##### App.svelte
```svelte
```
##### Flow\.svelte
```svelte
```
##### index.css
```css
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
```
##### index.html
```html
SvelteFlow Dagre Layout Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### nodes-edges.js
```js
export const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'input' },
position: { x: 0, y: 0 },
},
{
id: '2',
data: { label: 'node 2' },
position: { x: 0, y: 100 },
},
{
id: '2a',
data: { label: 'node 2a' },
position: { x: 0, y: 200 },
},
{
id: '2b',
data: { label: 'node 2b' },
position: { x: 0, y: 300 },
},
{
id: '2c',
data: { label: 'node 2c' },
position: { x: 0, y: 400 },
},
{
id: '2d',
data: { label: 'node 2d' },
position: { x: 0, y: 500 },
},
{
id: '3',
data: { label: 'node 3' },
position: { x: 200, y: 100 },
},
];
export const initialEdges = [
{ id: 'e12', source: '1', target: '2', animated: true },
{ id: 'e13', source: '1', target: '3', animated: true },
{ id: 'e22a', source: '2', target: '2a', animated: true },
{ id: 'e22b', source: '2', target: '2b', animated: true },
{ id: 'e22c', source: '2', target: '2c', animated: true },
{ id: 'e2c2d', source: '2c', target: '2d', animated: true },
];
```
With no effort at all we get a well-organized tree layout! Whenever
`getLayoutedElements` is called, we'll reset the dagre graph and set the graph's
direction (either left-to-right or top-to-bottom) based on the `direction` prop.
Dagre needs to know the dimensions of each node in order to lay them out, so we
iterate over our list of nodes and add them to dagre's internal graph.
After laying out the graph, we'll return an object with the layouted nodes and
edges. We do this by mapping over the original list of nodes and updating each
node's position according to node stored in the dagre graph.
Documentation for dagre's configuration options can be found
[here](https://github.com/dagrejs/dagre/wiki#configuring-the-layout), including
properties to set for spacing and alignment.
##### D3-Hierarchy
* Repo:
* Docs:
When you know your graph is a tree with a single root node, d3-hierarchy can
provide a handful of interesting layouting options. While the library can layout
a simple tree just fine, it also has layouting algorithms for tree maps, partition
layouts, and enclosure diagrams.
Example: learn/layouting-flow-3-d3-hierarchy
##### App.svelte
```svelte
```
##### Flow\.svelte
```svelte
```
##### index.css
```css
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
```
##### index.html
```html
SvelteFlow MiniMap Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### nodes-edges.js
```js
export const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'input' },
position: { x: 0, y: 0 },
},
{
id: '2',
data: { label: 'node 2' },
position: { x: 0, y: 100 },
},
{
id: '2a',
data: { label: 'node 2a' },
position: { x: 0, y: 200 },
},
{
id: '2b',
data: { label: 'node 2b' },
position: { x: 0, y: 300 },
},
{
id: '2c',
data: { label: 'node 2c' },
position: { x: 0, y: 400 },
},
{
id: '2d',
data: { label: 'node 2d' },
position: { x: 0, y: 500 },
},
{
id: '3',
data: { label: 'node 3' },
position: { x: 200, y: 100 },
},
];
export const initialEdges = [
{ id: 'e12', source: '1', target: '2', animated: true },
{ id: 'e13', source: '1', target: '3', animated: true },
{ id: 'e22a', source: '2', target: '2a', animated: true },
{ id: 'e22b', source: '2', target: '2b', animated: true },
{ id: 'e22c', source: '2', target: '2c', animated: true },
{ id: 'e2c2d', source: '2c', target: '2d', animated: true },
];
```
D3-hierarchy expects your graphs to have a single root node, so it won't work in all
cases. It's also important to note that d3-hierarchy assigns the same width and height
to _all_ nodes when calculating the layout, so it's not the best choice if you're
displaying lots of different node types.
##### D3-Force
* Repo:
* Docs:
For something more interesting than a tree, a force-directed layout might be
the way to go. D3-Force is a physics-based layouting library that can be used to
position nodes by applying different forces to them.
As a consequence, it's a little more complicated to configure and use compared to
dagre and d3-hierarchy. Importantly, d3-force's layouting algorithm is iterative,
so we need a way to keep computing the layout across multiple renders.
First, let's see what it does:
Example: learn/layouting-flow-4-d3-force
##### App.svelte
```svelte
```
##### Flow\.svelte
```svelte
```
##### collide.js
```js
import { quadtree } from 'd3-quadtree';
export function collide() {
let nodes = [];
let force = (alpha) => {
const tree = quadtree(
nodes,
(d) => d.x,
(d) => d.y,
);
for (const node of nodes) {
const r = node.measured.width / 2;
const nx1 = node.x - r;
const nx2 = node.x + r;
const ny1 = node.y - r;
const ny2 = node.y + r;
tree.visit((quad, x1, y1, x2, y2) => {
if (!quad.length) {
do {
if (quad.data !== node) {
const r = node.measured.width / 2 + quad.data.width / 2;
let x = node.x - quad.data.x;
let y = node.y - quad.data.y;
let l = Math.hypot(x, y);
if (l < r) {
l = ((l - r) / l) * alpha;
node.x -= x *= l;
node.y -= y *= l;
quad.data.x += x;
quad.data.y += y;
}
}
} while ((quad = quad.next));
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
}
};
force.initialize = (newNodes) => (nodes = newNodes);
return force;
}
export default collide;
```
##### index.css
```css
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
```
##### index.html
```html
SvelteFlow MiniMap Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### nodes-edges.js
```js
export const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'input' },
position: { x: 0, y: 0 },
},
{
id: '2',
data: { label: 'node 2' },
position: { x: 0, y: 100 },
},
{
id: '2a',
data: { label: 'node 2a' },
position: { x: 0, y: 200 },
},
{
id: '2b',
data: { label: 'node 2b' },
position: { x: 0, y: 300 },
},
{
id: '2c',
data: { label: 'node 2c' },
position: { x: 0, y: 400 },
},
{
id: '2d',
data: { label: 'node 2d' },
position: { x: 0, y: 500 },
},
{
id: '3',
data: { label: 'node 3' },
position: { x: 200, y: 100 },
},
];
export const initialEdges = [
{ id: 'e12', source: '1', target: '2', animated: true },
{ id: 'e13', source: '1', target: '3', animated: true },
{ id: 'e22a', source: '2', target: '2a', animated: true },
{ id: 'e22b', source: '2', target: '2b', animated: true },
{ id: 'e22c', source: '2', target: '2c', animated: true },
{ id: 'e2c2d', source: '2c', target: '2d', animated: true },
];
```
In our Svelte implementation, we use Svelte's lifecycle hooks and reactive state to manage the force simulation. The simulation is configured with a number of different forces applied so you can see how they interact: play around in your own code to see how you want to configure those forces. You can find the documentation and some different examples of d3-force [here](https://d3js.org/d3-force).
Rectangular collisions
D3-Force has a built-in collision force, but it assumes nodes are circles. We've thrown
together a custom force in `collision.js` that uses a similar algorithm but accounts for
our rectangular nodes instead. Feel free to steal it or let us know if you have any
suggestions for improvements!
The tick function progresses the simulation by one step and then updates Svelte
Flow with the new node positions. We've also included a demonstration on how to
handle node dragging while the simulation is running: if your flow isn't interactive
you can ignore that part!
For larger graphs, computing the force layout every render forever is going to incur a
big performance hit. In this example we have a simple toggle to turn the layouting on
and off, but you might want to come up with some other approach to only compute the
layout when necessary.
##### Elkjs
* Repo:
* Docs: (good luck!)
Elkjs is certainly the most configurable option available, but it's also the most
complicated. Elkjs is a Java library that's been ported to JavaScript, and it
provides a huge number of options for configuring the layout of your graph.
Example: learn/layouting-flow-6-elkjs
##### App.svelte
```svelte
```
##### Flow\.svelte
```svelte
```
##### index.css
```css
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
}
```
##### index.html
```html
SvelteFlow MiniMap Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### nodes-edges.js
```js
export const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'input' },
position: { x: 0, y: 0 },
},
{
id: '2',
data: { label: 'node 2' },
position: { x: 0, y: 100 },
},
{
id: '2a',
data: { label: 'node 2a' },
position: { x: 0, y: 200 },
},
{
id: '2b',
data: { label: 'node 2b' },
position: { x: 0, y: 300 },
},
{
id: '2c',
data: { label: 'node 2c' },
position: { x: 0, y: 400 },
},
{
id: '2d',
data: { label: 'node 2d' },
position: { x: 0, y: 500 },
},
{
id: '3',
data: { label: 'node 3' },
position: { x: 200, y: 100 },
},
];
export const initialEdges = [
{ id: 'e12', source: '1', target: '2', animated: true },
{ id: 'e13', source: '1', target: '3', animated: true },
{ id: 'e22a', source: '2', target: '2a', animated: true },
{ id: 'e22b', source: '2', target: '2b', animated: true },
{ id: 'e22c', source: '2', target: '2c', animated: true },
{ id: 'e2c2d', source: '2c', target: '2d', animated: true },
];
```
At its most basic we can compute layouts similar to dagre, but because the layouting
algorithm runs asynchronously we need to handle promises and update our Svelte state accordingly.
The ELK reference is your new best friend
We don't often recommend elkjs because its complexity makes it difficult for us to
support folks when they need it. If you do decide to use it, you'll want to keep the
original [Java API reference](https://eclipse.dev/elk/reference.html) handy.
We've also included a few examples of some of the other layouting algorithms
available, including a non-interactive force layout.
##### Honourable Mentions
Of course, we can't go through every layouting library out there: we'd never work
on anything else! Here are some other libraries we've come across that might be
worth taking a look at:
* If you want to use dagre or d3-hierarchy but need to support nodes with different
dimensions, both [d3-flextree](https://github.com/klortho/d3-flextree) and
[entitree-flex](https://github.com/codeledge/entitree-flex) look promising.
* [Cola.js](https://github.com/tgdwyer/WebCola) looks like a promising option for
so-called "constraint-based" layouts. We haven't had time to properly investigate
it yet, but it looks like you can achieve results similar to d3-force but with
a lot more control.
#### Routing Edges
If you don't have any requirements for edge routing, you can use one of the
layouting libraries above to position nodes and let the edges fall wherever they
may. Otherwise, you'll want to look into some libraries and techniques for edge
routing.
Your options here are more limited than for node layouting, but here are some
resources we thought looked promising:
* [Routing Orthogonal Diagram Connectors in JavaScript](https://medium.com/swlh/routing-orthogonal-diagram-connectors-in-javascript-191dc2c5ff70)
If you do explore some custom edge routing options, consider contributing back to
the community by writing a blog post or creating a library!
You can use the editable edges functionality in Svelte Flow as a starting point for implementing a custom edge that can be routed along a specific path.
### Sub Flows
A sub flow is a flowgraph contained within a node. These nested flows can operate
independently or connect with nodes outside their parent node, making them perfect for
organizing and grouping related nodes. This guide will show you how to implement sub flows
and explain the available options for child nodes.
##### Defining Child Nodes
To define a child node, use the `parentId` option (see all available options in the
[node options section](/api-reference/types/node)). Child nodes are positioned relative to
their parent, with `{ x: 0, y: 0 }` representing the top-left corner of the parent node.
```js highlight="parentId: 'A'"
let nodes = $state.raw([
{
id: 'A',
data: { label: 'parent' },
position: { x: 0, y: 0 },
},
{
id: 'B',
data: { label: 'child' },
position: { x: 10, y: 10 },
parentId: 'A',
},
]);
```
Order of Nodes:
Parents have to appear before their child nodes in the `nodes` array!
###### `extent: 'parent'`
When a parent node has defined dimensions, you can restrict child node movement to stay
within the parent's boundaries by setting `extent: 'parent'`. This prevents child nodes
from being dragged outside their parent's area.
Example: learn/sub-flows
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
html,
body,
#app {
width: 100%;
height: 100%;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### Child Node Behavior
Child nodes maintain their relative position when the parent node is moved. While the
`parentId` option establishes the parent-child relationship, child nodes can still be
positioned outside their parent (unless `extent: 'parent'` is set). However, they will
always move with their parent when it's dragged.
In our examples, we use the built-in `group` node type for parent nodes, which provides a
transparent background with a border. You can also use
[any custom node type](/learn/layouting/sub-flows#using-a-custom-parent-node) as a
parent.
Let's explore more complex scenarios by adding additional nodes and edges. You can create
connections both within a group and between sub flows and outer nodes:
Example: learn/sub-flows-2
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
html,
body,
#app {
width: 100%;
height: 100%;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
##### Using a Custom Parent Node
To demonstrate the flexibility of parent nodes, let's modify our example by removing the
label from node B and adding child nodes. This example shows how you can use any node type
as a parent. We'll also set `draggable: false` on the child nodes to make them static.
Example: learn/sub-flows-3
##### App.svelte
```svelte
```
##### index.css
```css
@import './xy-theme.css';
html,
body {
margin: 0;
font-family: sans-serif;
}
html,
body,
#app {
width: 100%;
height: 100%;
}
.svelte-flow {
--xy-background-color: #f7f9fb;
}
```
##### index.html
```html
Svelte Flow Example
```
##### index.ts
```ts
import { mount } from 'svelte';
import App from './App.svelte';
import './index.css';
mount(App, {
target: document.getElementById('app')!,
});
```
### Migrate to v1
### Migrate to Svelte Flow 1.0
Svelte Flow 1.0 is built from the ground up with Svelte 5 and includes many new features and improvements. This guide will help you migrate from Svelte Flow 0.x to 1.0.
If you are looking for the Svelte Flow 0.x docs, please refer to
[legacy.svelteflow.dev](https://legacy.svelteflow.dev).
#### New features
* **[Reconnect edges](/examples/edges/reconnect-edge)**: You can reconnect your edges by using the new `` component. It can be used to add custom reconnection points on custom edges.
* **Keyboard navigation & A11y**: We added support for keyboard navigation and improved accessibility for screen readers. You can now tab through nodes and edges and move nodes with the arrow keys. Can be disabled via [**disableKeyboardA11y**](/api-reference/svelte-flow#disablekeyboarda11y)
* **[Click connect](/examples/edges/click-connect)**: You can now create a new connection by clicking on a handle one by one.
* **[Enhanced ViewportPortal](/api-reference/components/viewport-portal)**: You can now decide if you want to render something below or above the nodes & edges in the viewport.
* **Improved [fitView](/api-reference/hooks/use-svelte-flow#fitview)**: We finetuned the `fitView` function to better work with dynamically added nodes.
* **colorModeSSR** prop: You can pass a fallback color mode for server side rendering when colorMode is set to 'system'.
* [**elevateNodesOnSelect**](/api-reference/svelte-flow#elevateNodesOnSelect) & [**elevateEdgesOnSelect**](/api-reference/svelte-flow#elevateEdgesOnSelect): Control if nodes & edges should be elevated via z-index when selected.
* [**noDragClass, noWheelClass, noPanClass**](/api-reference/svelte-flow#style-props): You can now modify the class name used to disable dragging, panning and zooming.
* [**onselectionchange**](/api-reference/svelte-flow#onselectionchange) & [**useOnSelectionChange**](/api-reference/hooks/use-on-selection-change): You can now listen to selection changes via a callback
#### Breaking changes
##### `nodes` & `edges` are now using `$state.raw` instead of `writable`
Svelte 5 introduces runes which are now getting used for nodes and edges.
**Old API**
```js
const nodes = writable([...]);
const edges = writable([...]);
```
**New API**
```js
let nodes = $state.raw([...]);
let edges = $state.raw([...]);
```
##### Updating Nodes & Edges
Previously it was possible to update single node properties. Theoretically, this would also be possible with `$state`, however the [performance implications](https://svelte.dev/playground/e6f804ba6da348bc8b6a0a13c59672cb?version=5.19.0) of this are unfortunately too great, so we opted to using `$state.raw`.
This means that `nodes` and `edges` are to be treated as immutable from now on. If you are making updates manually make sure you:
1. create a new node/edge object, when updating a property.
2. reassign the nodes/edges array (this was technically required before anyway)
```js
nodes[0].position.x = 100; // won't work
const newNode = { ...nodes[0] };
newNode.position.x = 100;
nodes[0] = newNode; // not enough to trigger an update
nodes = [...nodes]; // this will make it work
nodes = nodes.map((node) => {
if (node.id === '1') {
return { ...node, position: { ...node.position, x: 100 } };
}
return node;
}); // also works
updateNode('1', (node) => ({
...node,
position: { ...node.position, x: 100 },
})); // using the updateNode helper from useSvelteFlow
```
##### `nodes` & `edges` need to be bound from ``
**Old API**
```js
```
**New API**
```js
```
If `nodes` and `edges` live in a separate module, you can use [function bindings](https://svelte.dev/docs/svelte/bind#Function-bindings).
```js
// store.svelte.js
let nodes = $state.raw([...]);
let edges = $state.raw([...]);
export const getNodes = () => nodes;
export const getEdges = () => edges;
export const setNodes = (newNodes) => nodes = newNodes;
export const setEdges = (newEdges) => edges = newEdges;
```
```js
// BaseComponent.svelte
```
##### Custom Node & Edge Props
This is by enlarge a general change in Svelte 5, but it does have quite a big impact on typing the props of Custom Nodes & Edges.
**Old API**
```js
// CustomNode.svelte
type $$Props = NodeProps;
export let data: $$Props['data'];
export let position: $$Props['position'];
export let selected: $$Props['selected'];
```
**New API**
```js
let { data, position, selected } : NodeProps = $props();
```
##### Hooks
Hooks now return reactive values instead of writables. Because `$state` values cannot be [returned by functions directly](https://svelte.dev/docs/svelte/$state#Passing-state-into-functions) we have to return an object with a `.current` property to keep reactivity. In this regard, we are [following the official trend](https://svelte.dev/docs/svelte/svelte-reactivity#MediaQuery) set by the Svelte library authors.
**Old API**
```js
const edges = useEdges();
$: console.log(edges);
```
**New API**
```js
const edges = useEdges();
$inspect(edges.current);
```
Note that in case of `useNodes`, `useEdges` and `useViewport` reassignments to `.current` work!
```js
const nodes = useNodes();
function updateNodes() {
nodes.current = [...]
}
```
##### Binding the viewport
Binding the viewport now works natively in Svelte 5. You can either access the internal viewport or bind your very own viewport object to be used instead.
**Old API**
```js
const viewport = writable({ x: 100, y: 100, zoom: 1.25 });
```
**New API**
```js
let viewport = $state < Viewport > { x: 100, y: 100, zoom: 1.25 };
;
```
##### Custom Connection Line
Using a custom Connection Line was possible before by passing it to a [slot](https://svelte.dev/docs/svelte/legacy-slots). In Svelte Flow 1.0 we introduced a new prop called `connectionLineComponent`for this.
**Old API**
```js
```
**New API**
```js
```
##### `onEdgeCreate` becomes `onbeforeconnect`
`onedgecreate` was called before a new edge was created. This is now called `onbeforeconnect` to better align with events like [`onbeforeconnect`](/api-reference/svelte-flow#onbeforedelete).
**Old API**
```js
({...connection, id: crypto.randomUUID()})}
/>
```
**New API**
```js
({ ...connection, id: crypto.randomUUID() })}
/>
```
##### `` becomes [``](/api-reference/components/edge-label)
The `EdgeLabelRenderer` component is now called `EdgeLabel`. As it was just a simple Portal to begin with, the naming of it being a "renderer" was a bit misleading.
To add to this, the new `EdgeLabel` component now also handles clicks on the label automatically and is aware of what edge it belongs to.
**Old API**
```js filename="CustomEdge.svelte"
```
### Remove Attribution
If youβre considering removing the attribution, weβd first like to mention:
**If youβre using Svelte Flow at your organization and making money from it**, we rely on
your support to keep Svelte Flow developed and maintained under an MIT License. Before you
remove the attribution,
[see the ways you can support Svelte Flow to keep it running](/pro).
**Are you using Svelte Flow for a personal project?** Great! Go ahead and remove the
attribution. You can support us by reporting any bugs you find, sending us screenshots of
your projects, and starring us on [Github](https://github.com/xyflow/xyflow). If you start
making money using Svelte Flow or use it in an organization in the future, we would ask
that you re-add the attribution or become a Github or Open Collective Sponsor.
Thank you for supporting us βπ»
* [the xyflow team](https://xyflow.com/about)
To remove our attribution in the corner of your application you can pass `hideAttribution`
through `proOptions` prop to the `` component.
```svelte filename="App.svelte"
```
## Examples
## API Reference
### API Reference
This reference attempts to document every function, hook, component, and type exported by
Svelte Flow. If you are looking for guides, please refer to our [learn section](/learn).
#### How to use this reference
We think that documentation should answer two broad questions: "what is this thing?" and
"how do I use it?"
To that end, our API reference aims to **concisely** answer that first question, while
guides go into more detail on the second. If you find yourself clicking around the
reference wondering what the heck any of this means, maybe we have a guide that can help
you out!
#### A note for JavaScript users
Svelte Flow is written in TypeScript, but we know that not everyone uses it. We encourage
developers to use the technology that works best for them, and throughout our
documentation there is a blend of TypeScript and JavaScript examples.
For our API reference, however, we use TypeScript's syntax to document the types of props
and functions. Here's a quick crash course on how to read it:
β’ `?` means that the field or argument is optional.
β’ `` in a type definition represents a generic type parameter. Like a function argument
but for types! The definition `type Array = ...` means a type called `Array` that takes
a generic type parameter `T`.
β’ `` when referring to a type is like "filling in" a generic type parameter. It's like
calling a function but for types! The type `Array` is the type `Array` with the
generic type parameter `T` filled in with the type `number`.
β’ `T | U` means that the type is either `T` or `U`: this is often called a *union*.
β’ `T & U` means that the type is both `T` and `U`: this is often called an *intersection*.
The TypeScript folks have their own
[handy guide for reading types](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
that you might find useful. If you're still stuck on something, feel free to drop by our
[Discord](https://discord.com/invite/RVmnytFmGW) and ask for help!
### The SvelteFlowProvider component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/SvelteFlowProvider/SvelteFlowProvider.svelte)
The `` component wraps its child nodes with a Svelte context that
makes it possible to access a flow's internal state outside of the
[``](/api-reference/svelte-flow) component. Many of the hooks we
provide rely on this component to work.
```svelte filename="App.svelte"
```
```svelte filename="Sidebar.svelte"
```
The state provided by `` is first initialized with default values.
Only after the `` component initializes, will the state be replaced with
correct values. However, you can expect this to happen before the first render.
#### Notes
* If you're using a router and want your flow's state to persist across routes,
it's vital that you place the `` component *outside* of
your router.
* If you have multiple flows on the same page you will need to use a separate
``.
### The SvelteFlow component
### \
[Source on Github](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte)
The `` component is the heart of your Svelte Flow application.
```svelte
```
This component takes a lot of different props, most of which are optional. We've tried to
document them in groups that make sense to help you find your way.
#### Common props
These are the props you will most commonly use when working with Svelte Flow.
* `...props: HTMLAttributes`
#### Viewport props
* `viewport?: Viewport` Custom viewport to be used instead of internal one
* `initialViewport?: Viewport` Sets the initial position and zoom of the viewport.
If a default viewport is provided but fitView is enabled, the default viewport will be ignored.
* `fitView?: boolean` If set, initial viewport will show all nodes & edges
* `fitViewOptions?: FitViewOptionsBase` Options to be used in combination with fitView
* `minZoom?: number` Minimum zoom level
* `maxZoom?: number` Maximum zoom level
* `snapGrid?: SnapGrid` Grid all nodes will snap to
* `onlyRenderVisibleElements?: boolean` You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport.
This might improve performance when you have a large number of nodes and edges but also adds an overhead.
* `translateExtent?: CoordinateExtent` By default the viewport extends infinitely. You can use this prop to set a boundary.
The first pair of coordinates is the top left boundary and the second pair is the bottom right.
* `preventScrolling?: boolean` Disabling this prop will allow the user to scroll the page even when their pointer is over the flow.
* `attributionPosition?: PanelPosition` Set position of the attribution
#### Node props
* `nodeOrigin?: NodeOrigin` Defines nodes relative position to its coordinates
* `nodesDraggable?: boolean` Controls if all nodes should be draggable
* `nodesConnectable?: boolean` Controls if all nodes should be connectable to each other
* `nodesFocusable?: boolean` When `true`, focus between nodes can be cycled with the `Tab` key and selected with the `Enter`
key. This option can be overridden by individual nodes by setting their `focusable` prop.
* `nodeDragThreshold?: number` With a threshold greater than zero you can control the distinction between node drag and click events.
If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired.
* `nodeClickDistance?: number` Distance that the mouse can move between mousedown/up that will trigger a click
* `nodeExtent?: CoordinateExtent` By default the nodes can be placed anywhere. You can use this prop to set a boundary.
The first pair of coordinates is the top left boundary and the second pair is the bottom right.
* `elevateNodesOnSelect?: boolean` Enabling this option will raise the z-index of nodes when they are selected.
#### Edge props
* `edgesFocusable?: boolean` When `true`, focus between edges can be cycled with the `Tab` key and selected with the `Enter`
key. This option can be overridden by individual edges by setting their `focusable` prop.
* `elevateEdgesOnSelect?: boolean` Enabling this option will raise the z-index of edges when they are selected,
or when the connected nodes are selected.
* `defaultMarkerColor?: string | null` Color of edge markers
You can pass `null` to use the CSS variable `--xy-edge-stroke` for the marker color.
* `defaultEdgeOptions?: DefaultEdgeOptions` Defaults to be applied to all new edges that are added to the flow.
Properties on a new edge will override these defaults if they exist.
#### Event handlers
##### General Events
* `oninit?: () => void` This handler gets called when the flow is finished initializing
* `onflowerror?: OnError` Occasionally something may happen that causes Svelte Flow to throw an error.
Instead of exploding your application, we log a message to the console and then call this event handler.
You might use it for additional logging or to show a message to the user.
* `ondelete?: OnDelete` This handler gets called when the user deletes nodes or edges.
* `onbeforedelete?: OnBeforeDelete` This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false.
##### Node Events
* `onnodeclick?: NodeEventWithPointer` This event handler is called when a user clicks on a node.
* `onnodedragstart?: NodeTargetEventWithPointer` This event handler is called when a user starts to drag a node.
* `onnodedrag?: NodeTargetEventWithPointer` This event handler is called when a user drags a node.
* `onnodedragstop?: NodeTargetEventWithPointer` This event handler is called when a user stops dragging a node.
* `onnodepointerenter?: NodeEventWithPointer` This event handler is called when the pointer of a user enters a node.
* `onnodepointermove?: NodeEventWithPointer` This event handler is called when the pointer of a user moves over a node.
* `onnodepointerleave?: NodeEventWithPointer` This event handler is called when the pointer of a user leaves a node.
* `onnodecontextmenu?: NodeEventWithPointer` This event handler is called when a user right-clicks on a node.
##### Edge Events
* `onedgeclick?: ({ edge, event }: { edge: Edge; event: MouseEvent; }) => void` This event handler is called when a user clicks an edge.
* `onedgecontextmenu?: ({ edge, event }: { edge: Edge; event: MouseEvent; }) => void` This event handler is called when a user right-clicks an edge.
* `onedgepointerenter?: ({ edge, event }: { edge: Edge; event: PointerEvent; }) => void` This event handler is called when the pointer of a user enters an edge.
* `onedgepointerleave?: ({ edge, event }: { edge: Edge; event: PointerEvent; }) => void` This event handler is called when the pointer of a user enters an edge.
* `onreconnect?: OnReconnect` This event gets fired when after an edge was reconnected
* `onreconnectstart?: OnReconnectStart` This event gets fired when a user starts to reconnect an edge
* `onreconnectend?: OnReconnectEnd` This event gets fired when a user stops reconnecting an edge
* `onbeforereconnect?: OnBeforeReconnect` This handler gets called when an edge is reconnected. You can use it to modify the edge before the update is applied.
##### Selection Events
* `onselectionchanged: unknown`
* `onselectionclick?: NodesEventWithPointer` This event handler is called when a user clicks the selection box.
* `onselectioncontextmenu?: NodesEventWithPointer` This event handler is called when a user right-clicks the selection box.
* `onselectiondragstart?: OnSelectionDrag` This event handler gets called when a user starts to drag a selection box.
* `onselectiondrag?: OnSelectionDrag` This event handler gets called when a user drags a selection box.
* `onselectiondragstop?: OnSelectionDrag` This event handler gets called when a user stops dragging a selection box.
* `onselectionstart?: (event: PointerEvent) => void` This event handler gets called when the user starts to drag a selection box
* `onselectionend?: (event: PointerEvent) => void` This event handler gets called when the user finishes dragging a selection box
##### Pane Events
* `onpaneclick?: ({ event }: { event: MouseEvent; }) => void` This event handler is called when a user clicks the pane.
* `onpanecontextmenu?: ({ event }: { event: MouseEvent; }) => void` This event handler is called when a user right-clicks the pane.
* `onmovestart?: OnMove` This event handler is called when the user begins to pan or zoom the viewport
* `onmove?: OnMove` This event handler is called when the user pans or zooms the viewport
* `onmoveend?: OnMove` This event handler is called when the user stops panning or zooming the viewport
##### Connection Events
* `onconnect?: OnConnect` This event gets fired when a connection successfully completes and an edge is created.
* `onconnectstart?: OnConnectStart` When a user starts to drag a connection line, this event gets fired.
* `onbeforeconnect?: OnBeforeConnect` This handler gets called when a new edge is created. You can use it to modify the newly created edge.
* `onconnectend?: OnConnectEnd` When a user stops dragging a connection line, this event gets fired.
* `isValidConnection?: IsValidConnection`
* `clickConnect?: boolean` Toggles ability to make connections via clicking the handles
* `onclickconnectstart?: OnConnectStart` A connection is started by clicking on a handle
* `onclickconnectend?: OnConnectEnd` A connection is finished by clicking on a handle
#### Connection line props
* `connectionRadius?: number` The radius around a handle where you drop a connection line to create a new edge.
* `connectionLineComponent?: Component<{}, {}, string>` Provide a custom snippet to be used instead of the default connection line
* `connectionLineType?: ConnectionLineType` Choose from the built-in edge types to be used for connections
* `connectionLineStyle?: string` Styles to be applied to the connection line
* `connectionLineContainerStyle?: string` Styles to be applied to the container of the connection line
#### Interaction props
* `elementsSelectable?: boolean` Controls if all elements should (nodes & edges) be selectable
* `autoPanOnConnect?: boolean` You can enable this prop to automatically pan the viewport while making a new connection.
* `autoPanOnNodeDrag?: boolean` You can enable this prop to automatically pan the viewport while dragging a node.
* `selectNodesOnDrag?: boolean` Controls if nodes should be automatically selected when being dragged
* `panOnDrag?: boolean | number[]` Enabling this prop allows users to pan the viewport by clicking and dragging.
You can also set this prop to an array of numbers to limit which mouse buttons can activate panning.
* `selectionOnDrag?: boolean` Select multiple elements with a selection box, without pressing down selectionKey.
* `selectionMode?: SelectionMode` When set to "partial", when the user creates a selection box by click and dragging
nodes that are only partially in the box are still selected.
* `panOnScroll?: boolean` Controls if the viewport should pan by scrolling inside the container
Can be limited to a specific direction with panOnScrollMode
* `panOnScrollMode?: PanOnScrollMode` This prop is used to limit the direction of panning when panOnScroll is enabled.
The "free" option allows panning in any direction.
* `zoomOnScroll?: boolean` Controls if the viewport should zoom by scrolling inside the container.
* `zoomOnPinch?: boolean` Controls if the viewport should zoom by pinching on a touch screen
* `zoomOnDoubleClick?: boolean` Controls if the viewport should zoom by double clicking somewhere on the flow
* `connectionMode?: ConnectionMode` 'strict' connection mode will only allow you to connect source handles to target handles.
'loose' connection mode will allow you to connect handles of any type to one another.
* `paneClickDistance?: number` Distance that the mouse can move between mousedown/up that will trigger a click
* `zIndexMode?: ZIndexMode` Used to define how z-indexing is calculated for nodes and edges.
'auto' is for selections and sub flows, 'basic' for selections only, and 'manual' for no auto z-indexing.
#### Keyboard props
* `deleteKey?: KeyDefinition | KeyDefinition[] | null` Pressing down this key deletes all selected nodes & edges.
* `selectionKey?: KeyDefinition | KeyDefinition[] | null` Pressing down this key you can select multiple elements with a selection box.
* `multiSelectionKey?: KeyDefinition | KeyDefinition[] | null` Pressing down this key you can select multiple elements by clicking.
* `zoomActivationKey?: KeyDefinition | KeyDefinition[] | null` If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false.
By setting this prop to null you can disable this functionality.
* `panActivationKey?: KeyDefinition | KeyDefinition[] | null` If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false.
By setting this prop to null you can disable this functionality.
* `disableKeyboardA11y?: boolean` You can use this prop to disable keyboard accessibility features such as selecting nodes or
moving selected nodes with the arrow keys.
#### Style props
Applying certain classes to elements rendered inside the canvas will change how
interactions are handled. These props let you configure those class names if you need to.
* `noPanClass?: string` If an element in the canvas does not stop mouse events from propagating, clicking and dragging
that element will pan the viewport. Adding the `"nopan"` class prevents this behavior and this
prop allows you to change the name of that class.
* `noDragClass?: string` If a node is draggable, clicking and dragging that node will move it around the canvas. Adding
the `"nodrag"` class prevents this behavior and this prop allows you to change the name of that
class.
* `noWheelClass?: string` Typically, scrolling the mouse wheel when the mouse is over the canvas will zoom the viewport.
Adding the `"nowheel"` class to an element in the canvas will prevent this behavior and this prop
allows you to change the name of that class.
#### Notes
* The props of this component get exported as `SvelteFlowProps`
### The Background component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Background/Background.svelte)
The `` component makes it convenient to render different types of
backgrounds common in node-based UIs. It comes with three variants: `lines`,
`dots` and `cross`.
```svelte
```
#### Props
The type for props of `` component is exported as `BackgroundProps`.
* `id?: string` When multiple backgrounds are present on the page, each one should have a unique id.
* `bgColor?: string` Color of the background
* `patternColor?: string` Color of the pattern
* `patternClass?: string` Class applied to the pattern
* `class?: ClassValue` Class applied to the container
* `gap?: number | [number, number]` The gap between patterns. Passing in a tuple allows you to control the x and y gap
independently.
* `size?: number` The radius of each dot or the size of each rectangle if `BackgroundVariant.Dots` or
`BackgroundVariant.Cross` is used. This defaults to 1 or 6 respectively, or ignored if
`BackgroundVariant.Lines` is used.
* `lineWidth?: number` The stroke thickness used when drawing the pattern.
* `variant?: BackgroundVariant` Variant of the pattern.
### The BaseEdge component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/edges/BaseEdge.svelte)
The `` component gets used internally for all the edges. It can be used inside
a custom edge and handles the invisible helper edge and the edge label for you.
```svelte filename="CustomEdge.svelte"
```
#### Props
The type for props of `` component is exported as `BaseEdgeProps`.
Additionally, it extends the props of ``.
* `...props: Omit, "d" | "path" | "markerStart" | "markerEnd">`
#### Notes
* If you want to use an edge marker with the `` component, you can pass the
`markerStart` or `markerEnd` props passed to your custom edge through to the
`` component. You can see all the props passed to a custom edge by looking
at the [`EdgeProps`](/api-reference/types/edge-props) type.
### The ControlButton component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte)
The `` component is used to render custom/ additional buttons
for the `` component.
```svelte
console.log('β‘οΈ')}>
β‘οΈ
```
#### Props
The type for props of `` component is exported as `ControlButtonProps`. Additionally, it extends the props of ``.
* `...props: HTMLButtonAttributes`
### The Controls component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Controls/Controls.svelte)
The `` component renders a small panel that contain convenient
buttons to zoom in, zoom out, fit the view, and lock the viewport.
```svelte
```
#### Props
The type for props of `` component is exported as `ControlsProps`.
Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
#### Notes
* To extend or customize the controls, you can use the [``](/api-reference/components/control-button)
component
### The Edge Label component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte)
The `` component is used in your custom edges
to display an edge label that selects the edge when it is clicked.
```svelte filename="CustomEdge.svelte"
{#if label}
{label}
{/if}
```
#### Props
The type for props of `` component is exported as `EdgeLabelProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
### EdgeReconnectAnchor component
### \
If you want a way to make your edges updatable, you can use 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
```svelte filename="CustomEdge.svelte"
{#if !reconnecting}
{/if}
{#if selected}
{/if}
```
This example renders invisible reconnection points. Naturally, you can also render an icon inside the `` component.
#### Props
The type for props of `` component is exported as `EdgeReconnectAnchorProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
### The EdgeToolbar component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/EdgeToolbar/EdgeToolbar.svelte)
The EdgeToolbar component can be used to display a toolbar on a side of the passed x and y
coordinates.
```svelte filename="CustomEdge.svelte"
```
#### Props
* `...props: HTMLAttributes`
#### Notes
* By default, the toolbar is only visible when an edge is selected. You can override this
behavior by setting the `isVisible` prop to `true`.
### The Handle component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/Handle/Handle.svelte)
The `` component is used in your [custom nodes](/learn/customization/custom-nodes)
to define connection points.
```svelte filename="CustomNode.svelte"
{data.label}
```
#### Props
The type for props of `` component is exported as `HandleProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
#### Examples
##### Custom handle with validation
You can create your own custom handles by wrapping the `` component.
This example shows a custom handle that only allows connections when the
connection source matches a given id.
```svelte
```
##### Style handles when connecting
The handle receives the additional class names `connecting` when the connection
line is above the handle and `valid` if the connection is valid. You can find an
example which uses these classes [here](/examples/interaction/validation).
##### Multiple handles
If you need multiple source or target handles you can achieve this by creating a
custom node. Normally you just use the id of a node for the `source` or `target`
of an edge. If you have multiple source or target handles you need to pass an id
to these handles. These ids can be used by an edge with the `sourceHandle` and
`targetHandle` options, so that you can connect a specific handle. If you have a
node with `id: 'node-1'` and a handle with `id: 'handle-1'` you can connect an edge to this handle
by defining it with `source: 'node-1'` and `sourceHandle: 'hadnle-1'`.
##### Dynamic handles
If you are programmatically changing the position or number of handles in your
custom node, you need to update the node internals with the
[`useUpdateNodeInternals`](/api-reference/hooks/use-update-node-internals) hook.
You can find an example of how to implement a custom node with multiple handles
in the [custom node guide](/learn/customization/custom-nodes) or in the
[custom node example](/examples/nodes/custom-node).
##### Custom handle styles
Since the handle is a div, you can use CSS to style it or pass a style prop to
customize a Handle. You can see this in the
[Add Node On Edge Drop](/examples/nodes/add-node-on-edge-drop) and
[Simple Floating Edges](/examples/edges/simple-floating-edges) examples.
### Components
### The MiniMap component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte)
The `` component can be used to render an overview of your flow. It
renders each node as an SVG element and visualizes where the current viewport is
in relation to the rest of the flow.
```svelte
```
#### Props
The type for props of `` component is exported as `MiniMapProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
#### Examples
##### Making the mini map interactive
By default, the mini map is non-interactive. To allow users to interact with the
viewport by panning or zooming the minimap, you can set either of the `zoomable`
or `pannable` (or both!) props to `true`.
```svelte
```
##### Customising mini map node color
The `nodeColor`, `nodeStrokeColor`, and `nodeClassName` props can be a function
that takes a [`Node`](/api-reference/types/node) and computes a value for the prop. This can
be used to customize the appearance of each mini map node.
This example shows how to color each mini map node based on the node's type:
```svelte
```
### The NodeResizeControl component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/487b13c9ad8903789f56c6fcfd8222f9cb74b812/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte)
To create your own resizing UI, you can use the `NodeResizeControl` component where you can pass children (such as icons).
#### Props
The type for props of `` component is exported as `NodeResizeControlProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
### The NodeResizer component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/487b13c9ad8903789f56c6fcfd8222f9cb74b812/packages/svelte/src/lib/plugins/NodeResizer/NodeResizer.svelte)
The `` component can be used to add a resize functionality to your
nodes. It renders draggable controls around the node to resize in all directions.
```svelte filename="CustomNode.svelte"
{data.label}
```
#### Props
The type for props of `` component is exported as `NodeResizerProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
#### Examples
Head over to the [example page](/examples/nodes/node-resizer) to see how this is done.
Example: examples/nodes/node-resizer
##### App.svelte
```svelte
```
##### CustomResizerNode.svelte
```svelte
{data.label}
```
##### ResizableNode.svelte
```svelte
{data.label}
```
##### ResizableNodeSelected.svelte
```svelte
{data.label}
```
##### xy-theme.css
```css
/* xyflow theme files. Delete these to start from our base */
.svelte-flow {
--xy-background-color: #f7f9fb;
/* Custom Variables */
--xy-theme-selected: #ff4000;
--xy-theme-hover: #c5c5c5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #e8e8e8;
/* Built-in Variables see https://svelteflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #ededed;
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d,
0px 0.51px 1.01px 0px #0000001a;
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #aaaaaa;
--xy-edge-label-color-default: #505050;
--xy-resize-background-color-default: #9e86ed;
--xy-node-background-color-default: #ffffff;
--xy-theme-panel-bg: #ffffff;
--xy-theme-panel-text: #111827;
--xy-theme-resize-handle-bg: #ffffff;
--xy-theme-focus-border: #d9d9d9;
--xy-theme-muted-bg: #f3f4f6;
--xy-theme-subtle-border: #d1d5db;
}
.svelte-flow.dark {
--xy-background-color: #101012;
--xy-node-background-color-default: #1a1b1e;
--xy-node-border-default: 1px solid #303238;
--xy-handle-background-color-default: #141519;
--xy-handle-border-color-default: #5a5d66;
--xy-edge-label-color-default: #e6e7ea;
--xy-theme-hover: #7a7d86;
--xy-theme-edge-hover: #f3f4f6;
--xy-theme-panel-bg: #141519;
--xy-theme-panel-text: #f3f4f6;
--xy-theme-resize-handle-bg: #141519;
--xy-theme-focus-border: #5f6470;
--xy-theme-muted-bg: #24262c;
--xy-theme-subtle-border: #3f434c;
--xy-node-boxshadow-default:
0px 6px 18px 0px rgba(0, 0, 0, 0.35), 0px 2px 6px 0px rgba(0, 0, 0, 0.3),
0px 1px 2px 0px rgba(0, 0, 0, 0.35);
--xy-theme-color-focus: #4b4e57;
--color-background: #1a1b1e;
--color-hover-bg: #24262c;
--color-disabled: #8b9099;
}
.svelte-flow {
background-color: var(--xy-background-color);
}
/* Customizing Default Theming */
.svelte-flow__node {
box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 12px;
flex-direction: column;
border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default));
}
.svelte-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: var(--xy-theme-focus-border);
}
.svelte-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node.selectable:hover,
.svelte-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.svelte-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.svelte-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9e86ed;
}
.svelte-flow.dark .svelte-flow__node-group {
background-color: rgba(123, 107, 148, 0.22);
border-color: #7b6b94;
}
.svelte-flow__edge.selectable:hover .svelte-flow__edge-path,
.svelte-flow__edge.selectable.selected .svelte-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.svelte-flow__handle {
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: var(--xy-handle-background-color-default);
}
.svelte-flow__handle.connectionindicator:focus,
.svelte-flow__handle.connectingfrom,
.svelte-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.svelte-flow__node:has(.svelte-flow__resize-control.line) {
border-radius: 0;
}
.svelte-flow__resize-control.handle {
background-color: var(--xy-theme-resize-handle-bg);
border: none;
border-radius: 0;
width: 5px;
height: 5px;
}
.svelte-flow__edge-label {
background: var(--xy-background-color);
color: var(--xy-edge-label-color-default);
}
/*
Custom Example CSS - This CSS is to improve the example experience.
You can remove it if you want to use the default styles.
New Theme Classes:
.xy-theme__button - Styles for buttons.
.xy-theme__input - Styles for text inputs.
.xy-theme__checkbox - Styles for checkboxes.
.xy-theme__select - Styles for dropdown selects.
.xy-theme__label - Styles for labels.
Use these classes to apply consistent theming across your components.
*/
:root {
--color-primary: #ff4000;
--color-background: #fefefe;
--color-hover-bg: #f6f6f6;
--color-disabled: #76797e;
}
/* Custom Button Styling */
.xy-theme__button-group {
display: flex;
align-items: center;
.xy-theme__button:first-child {
border-radius: 100px 0 0 100px;
}
.xy-theme__button:last-child {
border-radius: 0 100px 100px 0;
margin: 0;
}
}
.xy-theme__button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
height: 2.5rem;
padding: 0 1rem;
border-radius: 100px;
border: 1px solid var(--color-primary);
background-color: var(--color-background);
color: var(--color-primary);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.xy-theme__button.active:hover,
.xy-theme__button.active:active {
background-color: var(--color-primary);
opacity: 0.9;
}
.xy-theme__button:hover {
background-color: var(--xy-controls-button-background-color-hover-default);
}
.xy-theme__button:active {
background-color: var(--color-hover-bg);
}
.xy-theme__button:disabled {
color: var(--color-disabled);
opacity: 0.8;
cursor: not-allowed;
border: 1px solid var(--color-disabled);
}
.xy-theme__button > span {
margin-right: 0.2rem;
}
/* Add gap between adjacent buttons */
.xy-theme__button + .xy-theme__button {
margin-left: 0.3rem;
}
/* Example Input Styling */
.xy-theme__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 7px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
}
.xy-theme__input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Specific Checkbox Styling */
.xy-theme__checkbox {
appearance: none;
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 7px;
border: 2px solid var(--color-primary);
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
cursor: pointer;
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.xy-theme__checkbox:checked {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.xy-theme__checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
/* Dropdown Styling */
.xy-theme__select {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-primary);
border-radius: 50px;
background-color: var(--color-background);
transition:
background-color 0.2s ease,
border-color 0.2s ease;
font-size: 1rem;
color: var(--xy-theme-panel-text);
margin-right: 0.5rem;
box-shadow: var(--xy-node-boxshadow-default);
}
.xy-theme__select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3);
}
.xy-theme__label {
margin-top: 10px;
margin-bottom: 3px;
display: inline-block;
color: var(--xy-theme-panel-text);
}
```
##### index.css
```css
@import url('./xy-theme.css');
html,
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
```
##### Custom Resize Controls
To build custom resize controls, you can use the [NodeResizeControl](/api-reference/components/node-resize-control) component and customize it.
#### Notes
* Take a look at the docs for the [`NodeProps`](/api-reference/types/node-props) type or the
guide on [custom nodes](/learn/customization/custom-nodes) to see how to
implement your own nodes.
### The NodeToolbar component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte)
The NodeToolbar component can be used to display a toolbar on a side of a node or display a tooltip for example.
```svelte filename="CustomNode.svelte"
{data.label}
```
#### Props
The type for props of `` component is exported as `NodeToolbarProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
#### Notes
* By default, the toolbar is only visible when a node is selected. If multiple
nodes are selected it will not be visible to prevent overlapping toolbars or
clutter. You can override this behavior by setting the `isVisible` prop to
`true`.
### The Panel component
### \
[Source on GitHub](hthttps://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/Panel/Panel.svelte)
The `` component helps you position content above the viewport. It is
used internally by the [``](/api-reference/components/mini-map) and [``](/api-reference/components/controls)
components.
```svelte
top-lefttop-centertop-rightbottom-leftbottom-centerbottom-rightcenter-leftcenter-right
```
#### Props
The type for props of `` component is exported as `PanelProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
### The ViewportPortal component
### \
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/react/src/components/ViewportPortal/index.tsx)
`` component can be used to add components to the same viewport of the flow where nodes and edges are rendered.
This is useful when you want to render your own components that are adhere to the same coordinate system as the nodes & edges and are also
affected by zooming and panning
```svelte
This div is positioned at [100, 100] on the flow.
```
You can also define if you want to render the component below or above the nodes and edges by using the `target` prop.
```svelte
This div is positioned at [100, 100] on the flow.
```
#### Props
The type for props of `` component is exported as `ViewportPortalProps`. Additionally, it extends the props of ``.
* `...props: HTMLAttributes`
### Hooks
### useConnection()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useConnection.svelte.ts)
The `useConnection` hook returns the current connection when there is an active connection
interaction. If no connection interaction is active, it returns `null` for every property.
A typical use case for this hook is to colorize handles based on a certain condition (e.g.
if the connection is valid or not).
```svelte
```
#### Signature
Hook for receiving the current connection.
This function does not accept any parameters.
###### Returns
* `current: ConnectionState`
### useEdges()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts#L35)
The `useEdges` hook returns an array of the current edges.
```svelte
```
#### Signature
Hook for getting the current edges from the store.
This function does not accept any parameters.
###### Returns
* `current: Edge[]`
* `update: (updateFn: (edges: Edge[]) => Edge[]) => void`
* `set: (edges: Edge[]) => void`
### useInternalNode()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useInternalNode.svelte.ts)
The `useInternalNode` hook returns an internal node. An internal node is used for advanced
use cases like custom edges or layouting.
```svelte
```
#### Signature
Hook to get an internal node by id.
###### Parameters
* `id: string` the node id
###### Returns
* `current: InternalNode | undefined`
### useNodeConnections()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodeConnections.svelte.ts)
This hook returns an array of connections on a specific node, handle type ('source',
'target') or handle ID.
```svelte
There are currently {connections.length} incoming connections!
```
#### Signature
Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
###### Parameters
* `[0]?.id?: string`
* `[0]?.handleType?: 'source' | 'target'`
* `[0]?.handleId?: string`
* `[0]?.onConnect?: (connections: HandleConnection[]) => void`
* `[0]?.onDisconnect?: (connections: HandleConnection[]) => void`
###### Returns
* `current: NodeConnection[]`
### useNodesData()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodesData.svelte.ts)
With this hook you can receive the data of the passed node ids.
```svelte
```
#### Signature
Hook for receiving data of one or multiple nodes
###### Parameters
* `nodeId: string` The id (or ids) of the node to get the data from
###### Returns
* `current: DistributivePick | null`
#### Notes
* Check the [Computing Flows example](/examples/interaction/computing-flows) to see how
this hook can be used
### useNodesInitialized()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useInitialized.svelte.ts)
This hook can be used to check if all nodes are initialized. It returns a signal with a
boolean.
```svelte
```
#### Signature
Hook for seeing if nodes are initialized
This function does not accept any parameters.
###### Returns
* `current: boolean`
### useNodes()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useNodesEdgesViewport.svelte.ts)
This hook returns the current nodes array. When you subscribe, it will trigger whenever
the nodes array changes. This happens when nodes are added, removed, or updated (dragged
for example).
```svelte
```
#### Signature
Hook for getting the current nodes from the store.
This function does not accept any parameters.
###### Returns
* `current: Node[]`
* `update: (updateFn: (nodes: Node[]) => Node[]) => void`
* `set: (nodes: Node[]) => void`
### useOnSelectionChange()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useOnSelectionChange.svelte.ts)
This hook lets you listen for changes to both node and edge selection. As the name
implies, the callback you provide will be called whenever the selection of *either* nodes
or edges changes.
```svelte filename="Component.svelte"
Selected nodes: {selectedNodes.join(', ')}
Selected edges: {selectedEdges.join(', ')}
```
#### Signature
###### Parameters
* `onselectionchange: OnSelectionChange`
###### Returns
`void`
#### Notes
* This hook can only be used in a component that is a child of a
[``](/api-reference/svelte-flow-provider) or a
[``](/api-reference/svelte-flow) component.
### useStore()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/store/index.ts/)
This hook can be used to access the internal store of the Svelte Flow.
This hook is only needed for advanced use cases. It should only be used if there is no
other way to access the internal state. For many of the common use cases, there are
dedicated hooks available such as
[`useConnection`](/api-reference/hooks/use-connection),
[`useNodes`](/api-reference/hooks/use-nodes), etc.
```svelte
```
#### Signature
This function does not accept any parameters.
###### Returns
`SvelteFlowStore`
### useSvelteFlow()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useSvelteFlow.svelte.ts)
This hook returns functions to update the viewport, transform positions or get node
intersections for example.
```svelte
```
#### Signature
Hook for accessing the SvelteFlow instance.
This function does not accept any parameters.
###### Returns
* `zoomIn: (options?: { duration?: number; ease?: (t: number) => number; interpolate?: "smooth" | "linear"; }) => Promise` Zooms viewport in by 1.2.
* `zoomOut: (options?: { duration?: number; ease?: (t: number) => number; interpolate?: "smooth" | "linear"; }) => Promise` Zooms viewport out by 1 / 1.2.
* `getInternalNode: (id: string) => InternalNode | undefined` Returns an internal node by id.
* `getNode: (id: string) => NodeType | undefined` Returns a node by id.
* `getNodes: (ids?: string[] | undefined) => NodeType[]` Returns nodes.
* `getEdge: (id: string) => EdgeType | undefined` Returns an edge by id.
* `getEdges: (ids?: string[] | undefined) => EdgeType[]` Returns edges.
* `setZoom: (zoomLevel: number, options?: ViewportHelperFunctionOptions | undefined) => Promise` Sets the current zoom level.
* `getZoom: () => number` Returns the current zoom level.
* `setCenter: (x: number, y: number, options?: SetCenterOptions | undefined) => Promise` Sets the center of the view to the given position.
* `setViewport: (viewport: Viewport, options?: ViewportHelperFunctionOptions | undefined) => Promise` Sets the current viewport.
* `getViewport: () => Viewport` Returns the current viewport.
* `fitView: (options?: FitViewOptions | undefined) => Promise` Fits the view.
* `getIntersectingNodes: (nodeOrRect: NodeType | { id: NodeType["id"]; } | Rect, partially?: boolean | undefined, nodesToIntersect?: NodeType[] | undefined) => NodeType[]` Returns all nodes that intersect with the given node or rect.
* `isNodeIntersecting: (nodeOrRect: NodeType | Rect | { id: NodeType["id"]; }, area: Rect, partially?: boolean | undefined) => boolean` Checks if the given node or rect intersects with the passed rect.
* `fitBounds: (bounds: Rect, options?: FitBoundsOptions | undefined) => Promise` Fits the view to the given bounds .
* `deleteElements: ({ nodes, edges }: { nodes?: (Partial & { id: string; })[]; edges?: (Partial & { id: string; })[]; }) => Promise<{ deletedNodes: NodeType[]; deletedEdges: EdgeType[]; }>` Deletes nodes and edges.
* `screenToFlowPosition: (clientPosition: XYPosition, options?: { snapToGrid: boolean; } | undefined) => XYPosition` Converts a screen / client position to a flow position.
* `flowToScreenPosition: (flowPosition: XYPosition) => XYPosition` Converts a flow position to a screen / client position.
* `updateNode: (id: string, nodeUpdate: Partial | ((node: NodeType) => Partial), options?: { replace: boolean; } | undefined) => void` Updates a node.
* `updateNodeData: (id: string, dataUpdate: Partial | ((node: NodeType) => Partial), options?: { replace: boolean; } | undefined) => void` Updates the data attribute of a node.
* `updateEdge: (id: string, edgeUpdate: Partial | ((edge: EdgeType) => Partial), options?: { replace: boolean; } | undefined) => void` Updates an edge.
* `toObject: () => { nodes: NodeType[]; edges: EdgeType[]; viewport: Viewport; }`
* `getNodesBounds: (nodes: (string | NodeType | InternalNode)[]) => Rect` Returns the bounds of the given nodes or node ids.
* `getHandleConnections: ({ type, id, nodeId }: { type: HandleType; nodeId: string; id?: string | null; }) => HandleConnection[]` Gets all connections for a given handle belonging to a specific node.
### useUpdateNodeInternals()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/hooks/useUpdateNodeInternals.svelte.ts/#L6)
When you programmatically add or remove handles to a node or update a node's handle
position, you need to inform Svelte Flow about it by using this hook. This will update the
internal dimensions of the node and properly reposition handles on the canvas if
necessary.
```svelte
```
#### Signature
When you programmatically add or remove handles to a node or update a node's
handle position, you need to let Svelte Flow know about it using this hook. This
will update the internal dimensions of the node and properly reposition handles
on the canvas if necessary.
This function does not accept any parameters.
###### Returns
`(nodeId?: string | string[] | undefined) => void`
### Align
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/nodes.ts#L174)
The `Align` type contains the values expected by the `align` prop of the [NodeToolbar](/api-reference/components/node-toolbar) component
```ts
export type Align = 'center' | 'start' | 'end';
```
### AriaLabelConfig
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/constants.ts/)
With the `AriaLabelConfig` you can customize the aria labels used by Svelte Flow. This is useful if you want to translate the labels or if you want to change them to better suit your application.
#### Fields
* `node.a11yDescription.default: string`
* `node.a11yDescription.keyboardDisabled: string`
* `node.a11yDescription.ariaLiveMessage: ({ direction, x, y }: { direction: string; x: number; y: number; }) => string`
* `edge.a11yDescription.default: string`
* `controls.ariaLabel: string`
* `controls.zoomIn.ariaLabel: string`
* `controls.zoomOut.ariaLabel: string`
* `controls.fitView.ariaLabel: string`
* `controls.interactive.ariaLabel: string`
* `minimap.ariaLabel: string`
* `handle.ariaLabel: string`
#### Default config
```js
const defaultAriaLabelConfig = {
'node.a11yDescription.default':
'Press enter or space to select a node. Press delete to remove it and escape to cancel.',
'node.a11yDescription.keyboardDisabled':
'Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.',
'node.a11yDescription.ariaLiveMessage': ({ direction, x, y }: { direction: string; x: number; y: number }) =>
`Moved selected node ${direction}. New position, x: ${x}, y: ${y}`,
'edge.a11yDescription.default':
'Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.',
// Control elements
'controls.ariaLabel': 'Control Panel',
'controls.zoomIn.ariaLabel': 'Zoom In',
'controls.zoomOut.ariaLabel': 'Zoom Out',
'controls.fitView.ariaLabel': 'Fit View',
'controls.interactive.ariaLabel': 'Toggle Interactivity',
// Mini map
'minimap.ariaLabel': 'Mini Map',
// Handle
'handle.ariaLabel': 'Handle',
};
```
### BackgroundVariant
The three variants are exported as an enum for convenience. You can either import the enum and use it like `BackgroundVariant.Lines` or you can use the raw string value directly.
```ts
export enum BackgroundVariant {
Lines = 'lines',
Dots = 'dots',
Cross = 'cross',
}
```
### ColorMode
The color mode type specifies if the current color mode is light, dark or uses system.
```ts
export type ColorMode = 'light' | 'dark' | 'system';
```
### ConnectionLineType
With the `connectionLineType` prop on your [``](/api-reference/svelte-flow#connection-connectionLineType) component, you can configure the type of the connection line. Svelte Flow comes with built-in support for the following types: 'default' (bezier), 'straight', 'step', 'smoothstep' and 'simplebezier'.
```ts
export enum ConnectionLineType {
Bezier = 'default',
Straight = 'straight',
Step = 'step',
SmoothStep = 'smoothstep',
SimpleBezier = 'simplebezier',
}
```
### ConnectionMode
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L68)
The `ConnectionMode` enum defines the available strategies for connecting nodes. Use `Strict` to only allow connections to valid handles, or `Loose` to allow more flexible connections.
```ts
enum ConnectionMode {
Strict = 'strict',
Loose = 'loose',
}
```
### Connection
The `Connection` type is the basic minimal description of an [`Edge`](/api-reference/types/edge) between two nodes.
#### Fields
The `Connection` type is the basic minimal description of an [`Edge`](/api-reference/types/edge)
between two nodes. The [`addEdge`](/api-reference/utils/add-edge) util can be used to upgrade
a `Connection` to an [`Edge`](/api-reference/types/edge).
* `source: string` The id of the node this connection originates from.
* `target: string` The id of the node this connection terminates at.
* `sourceHandle: string | null` When not `null`, the id of the handle on the source node that this connection originates from.
* `targetHandle: string | null` When not `null`, the id of the handle on the target node that this connection terminates at.
### CoordinateExtent
A coordinate extent represents two points in a coordinate system: one in the top left corner and one in the bottom right corner. It is used to represent the bounds of nodes in the flow or the bounds of the viewport.
```ts
export type CoordinateExtent = [[number, number], [number, number]];
```
#### Notes
* Props (like nodeExtent or translateExtent) that expect a `CoordinateExtent` usually default to `[[-β, -β], [+β, +β]]` to represent an unbounded extent.
### DefaultEdgeOptions
Many properties on an [`Edge`](/api-reference/types/edge) are optional. When a new edge is created,
the properties that are not provided will be filled in with the default values
passed to the `defaultEdgeOptions` prop of the [``](/api-reference/svelte-flow#defaultedgeoptions)
component.
#### Fields
* `type?: string | undefined` Type of edge defined in `edgeTypes`.
* `animated?: boolean`
* `hidden?: boolean`
* `deletable?: boolean`
* `selectable?: boolean`
* `data?: Record` Arbitrary data passed to an edge.
* `markerStart?: EdgeMarkerType` Set the marker on the beginning of an edge.
* `markerEnd?: EdgeMarkerType` Set the marker on the end of an edge.
* `zIndex?: number`
* `ariaLabel?: string`
* `interactionWidth?: number` ReactFlow renders an invisible path around each edge to make them easier to click or tap on.
This property sets the width of that invisible path.
* `label?: string`
* `labelStyle?: string`
* `style?: string`
* `class?: ClassValue`
* `focusable?: boolean`
* `ariaRole?: AriaRole | null | undefined` The ARIA role attribute for the edge, used for accessibility.
* `domAttributes?: Omit, "id" | "style" | "class" | "role" | "aria-label" | "dangerouslySetInnerHTML">` General escape hatch for adding custom attributes to the edge's DOM element.
### EdgeMarker
You can customize the built-in edge markers with the `edgeMarker` [Edge](/api-reference/types/edge) prop.
#### Fields
Edges can optionally have markers at the start and end of an edge. The `EdgeMarker`
type is used to configure those markers! Check the docs for [`MarkerType`](/api-reference/types/marker-type)
for details on what types of edge marker are available.
* `type: MarkerType | "arrow" | "arrowclosed"`
* `color?: string | null`
* `width?: number`
* `height?: number`
* `markerUnits?: string`
* `orient?: string`
* `strokeWidth?: number`
### EdgeProps
When you implement a custom edge it is wrapped in a component that enables some basic functionality. Your custom edge component receives the following props:
#### Fields
Custom edge component props.
* `id: EdgeType["id"]` Unique id of an edge.
* `type: EdgeType["type"] & string` Type of edge defined in `edgeTypes`.
* `source: EdgeType["source"]` Id of source node.
* `target: EdgeType["target"]` Id of target node.
* `animated?: EdgeType["animated"]`
* `hidden?: EdgeType["hidden"]`
* `deletable?: EdgeType["deletable"]`
* `selectable?: EdgeType["selectable"]`
* `data?: EdgeType["data"]` Arbitrary data passed to an edge.
* `selected?: EdgeType["selected"]`
* `markerStart?: EdgeType["markerStart"] & string` Set the marker on the beginning of an edge.
* `markerEnd?: EdgeType["markerEnd"] & string` Set the marker on the end of an edge.
* `zIndex?: EdgeType["zIndex"]`
* `ariaLabel?: EdgeType["ariaLabel"]`
* `interactionWidth?: EdgeType["interactionWidth"]` ReactFlow renders an invisible path around each edge to make them easier to click or tap on.
This property sets the width of that invisible path.
* `label?: EdgeType["label"]`
* `labelStyle?: EdgeType["labelStyle"]`
* `style?: EdgeType["style"]`
* `class?: EdgeType["class"]`
* `focusable?: EdgeType["focusable"]`
* `ariaRole?: EdgeType["ariaRole"]` The ARIA role attribute for the edge, used for accessibility.
* `domAttributes?: EdgeType["domAttributes"]` General escape hatch for adding custom attributes to the edge's DOM element.
* `sourceX: number`
* `sourceY: number`
* `targetX: number`
* `targetY: number`
* `sourcePosition: Position`
* `targetPosition: Position`
* `sourceHandleId?: string | null`
* `targetHandleId?: string | null`
### EdgeTypes
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/edges.ts#L140)
The `EdgeTypes` type is a record that maps edge type identifiers to their corresponding Svelte components. This allows you to define custom edge types and their implementations.
```ts
type EdgeTypes = Record;
```
### Edge
### Edge\
An `Edge` is the complete description with everything Svelte Flow needs to know in order to render it.
#### Fields
An `Edge` is the complete description with everything Svelte Flow needs to know in order to
render it.
* `id: string` Unique id of an edge.
* `type?: EdgeType` Type of edge defined in `edgeTypes`.
* `source: string` Id of source node.
* `target: string` Id of target node.
* `sourceHandle?: string | null` Id of source handle, only needed if there are multiple handles per node.
* `targetHandle?: string | null` Id of target handle, only needed if there are multiple handles per node.
* `animated?: boolean`
* `hidden?: boolean`
* `deletable?: boolean`
* `selectable?: boolean`
* `data?: EdgeData` Arbitrary data passed to an edge.
* `selected?: boolean`
* `markerStart?: EdgeMarkerType` Set the marker on the beginning of an edge.
* `markerEnd?: EdgeMarkerType` Set the marker on the end of an edge.
* `zIndex?: number`
* `ariaLabel?: string`
* `interactionWidth?: number` ReactFlow renders an invisible path around each edge to make them easier to click or tap on.
This property sets the width of that invisible path.
* `label?: string`
* `labelStyle?: string`
* `style?: string`
* `class?: ClassValue`
* `focusable?: boolean`
* `ariaRole?: AriaRole | null | undefined` The ARIA role attribute for the edge, used for accessibility.
* `domAttributes?: Omit, "role" | "id" | "style" | "class" | "aria-label" | "dangerouslySetInnerHTML">` General escape hatch for adding custom attributes to the edge's DOM element.
### FitViewOptions
When calling `fitView` these options can be used to customize the behavior. For example, the `duration` option can be used to transform the viewport smoothly over a given amount of time.
#### Fields
* `padding?: Padding`
* `includeHiddenNodes?: boolean`
* `minZoom?: number`
* `maxZoom?: number`
* `duration?: number`
* `ease?: (t: number) => number`
* `interpolate?: "smooth" | "linear"`
* `nodes?: (NodeType | { id: string; })[]`
### Types
### InternalNode
### InternalNode\
The internal node is an extension of the user node. It is used internally and has some more information that is not exposed to the user directly, like `positionAbsolute` and `handleBounds`.
#### Fields
The node data structure that gets used for internal nodes.
There are some data structures added under node.internal
that are needed for tracking some properties
* `width?: NodeType["width"]`
* `height?: NodeType["height"]`
* `id: NodeType["id"]` Unique id of a node.
* `position: NodeType["position"]` Position of a node on the pane.
* `type?: NodeType["type"]` Type of node defined in nodeTypes
* `data: NodeType["data"]` Arbitrary data passed to a node.
* `sourcePosition?: NodeType["sourcePosition"]` Only relevant for default, source, target nodeType. Controls source position.
* `targetPosition?: NodeType["targetPosition"]` Only relevant for default, source, target nodeType. Controls target position.
* `hidden?: NodeType["hidden"]` Whether or not the node should be visible on the canvas.
* `selected?: NodeType["selected"]`
* `dragging?: NodeType["dragging"]` Whether or not the node is currently being dragged.
* `draggable?: NodeType["draggable"]` Whether or not the node is able to be dragged.
* `selectable?: NodeType["selectable"]`
* `connectable?: NodeType["connectable"]`
* `deletable?: NodeType["deletable"]`
* `dragHandle?: NodeType["dragHandle"]` A class name that can be applied to elements inside the node that allows those elements to act
as drag handles, letting the user drag the node by clicking and dragging on those elements.
* `initialWidth?: NodeType["initialWidth"]`
* `initialHeight?: NodeType["initialHeight"]`
* `parentId?: NodeType["parentId"]` Parent node id, used for creating sub-flows.
* `zIndex?: NodeType["zIndex"]`
* `extent?: NodeType["extent"]` Boundary a node can be moved in.
* `expandParent?: NodeType["expandParent"]` When `true`, the parent node will automatically expand if this node is dragged to the edge of
the parent node's bounds.
* `ariaLabel?: NodeType["ariaLabel"]`
* `origin?: NodeType["origin"]` Origin of the node relative to its position.
* `handles?: NodeType["handles"]`
* `class?: NodeType["class"]`
* `style?: NodeType["style"]`
* `focusable?: NodeType["focusable"]`
* `ariaRole?: NodeType["ariaRole"]` The ARIA role attribute for the node element, used for accessibility.
* `domAttributes?: NodeType["domAttributes"]` General escape hatch for adding custom attributes to the node's DOM element.
* `measured: { width?: number; height?: number; }`
* `internals: { positionAbsolute: XYPosition; z: number; rootParentIndex?: number; userNode: NodeType; handleBounds?: NodeHandleBounds; bounds?: NodeBounds; }`
#### Notes
* The internal node can be accessed using the [`useInternalNode`](/api-reference/hooks/use-internal-node) hook or [`getInternalNode`](/api-reference/hooks/use-svelte-flow#get-internal-node).
### IsValidConnection
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L43)
The `IsValidConnection` type is a function that determines whether a connection between
nodes is valid. It receives an edge or connection object and returns a boolean indicating
whether the connection is valid.
```ts
type IsValidConnection = (edge: Edge | Connection) => boolean;
```
###### Parameters
* `edge: EdgeBase | Connection`
###### Returns
`boolean`
### KeyDefinition
A key definition is a string or an object that describes a key and a modifier. It is used for defining built-in keybindings like `selectionKey` or `deleteKey`.
```ts
export type KeyDefinitionObject = { key: string; modifier?: KeyModifier };
export type KeyDefinition = string | KeyDefinitionObject;
```
### MarkerType
Svelte Flow comes with two built-in markers: `MarkerType.Arrow` and `MarkerType.ArrowClosed`. You can use these by setting the `markerStart`/ `markerEnd` [Edge](/api-reference/types/edge) option.
```ts
export enum MarkerType {
Arrow = 'arrow',
ArrowClosed = 'arrowclosed',
}
```
### NodeConnection
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts/#L36-L37)
The `NodeConnection` type is an extension of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`.
#### Fields
The `NodeConnection` type is an extension of a basic [Connection](/api-reference/types/connection) that includes the `edgeId`.
* `source: string` The id of the node this connection originates from.
* `target: string` The id of the node this connection terminates at.
* `sourceHandle: string | null` When not `null`, the id of the handle on the source node that this connection originates from.
* `targetHandle: string | null` When not `null`, the id of the handle on the target node that this connection terminates at.
* `edgeId: string`
### NodeEventWithPointer
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/events.ts#4)
The `NodeEventWithPointer` type represents an event that occurs during node interactions, including pointer position and event details. It extends the base node event with pointer-specific information.
```ts
type NodeEventWithPointer = {
event: PointerEvent;
node: NodeType;
};
```
###### Parameters
* `__0: { node: NodeType; event: T; }`
###### Returns
`void`
### NodeOrigin
The origin of a Node determines how it is placed relative to its own coordinates.
`[0, 0]` places it at the top left corner, `[0.5, 0.5]` right in the center and `[1, 1]` at the bottom right of its position.
```ts
type NodeOrigin = [number, number];
```
### NodeProps
When you implement a [custom node](/learn/customization/custom-nodes) it is wrapped in a
component that enables basic functionality like drag, select and remove. A custom node
gets the following props:
* `id: NodeType["id"]` Unique id of a node.
* `data: NodeType["data"]` Arbitrary data passed to a node.
* `width?: NodeType["width"]`
* `height?: NodeType["height"]`
* `sourcePosition?: NodeType["sourcePosition"]` Only relevant for default, source, target nodeType. Controls source position.
* `targetPosition?: NodeType["targetPosition"]` Only relevant for default, source, target nodeType. Controls target position.
* `dragHandle?: NodeType["dragHandle"]` A class name that can be applied to elements inside the node that allows those elements to act
as drag handles, letting the user drag the node by clicking and dragging on those elements.
* `parentId?: NodeType["parentId"]` Parent node id, used for creating sub-flows.
* `type: any` Type of node defined in nodeTypes
* `dragging: NodeType["dragging"]` Whether or not the node is currently being dragged.
* `zIndex: NodeType["zIndex"]`
* `selectable: NodeType["selectable"]`
* `deletable: NodeType["deletable"]`
* `selected: NodeType["selected"]`
* `draggable: NodeType["draggable"]` Whether or not the node is able to be dragged.
* `isConnectable: boolean` Whether a node is connectable or not.
* `positionAbsoluteX: number` Position absolute x value.
* `positionAbsoluteY: number` Position absolute y value.
#### Notes
* If you have controls (like a slider or an input) or other elements inside your custom
node that **should not drag the node** you can add the class `nodrag` to those elements.
This prevents the default drag behavior as well as the default node selection behavior
when elements with this class are clicked.
```svelte filename="CustomNode.svelte"
```
* If you have scroll containers inside your custom node you can add the class `nowheel` to
**disable the default canvas pan behavior when scrolling** inside your custom nodes.
```svelte filename="CustomNode.svelte"
Scrollable content...
```
* When creating your own custom nodes, you will also need to remember to style them!
Custom nodes have no default styles unlike the built-in nodes so you can use any
[styling method you like](/learn/customization/theming).
### NodeTargetEventWithPointer
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/events.ts#20)
The `NodeTargetEventWithPointer` type represents an event that occurs during target node interactions, including pointer position and event details. It extends the base node event with pointer-specific information and target node data.
```ts
type NodeTargetEventWithPointer = {
event: PointerEvent;
node: NodeType;
targetNode: NodeType;
};
```
###### Parameters
* `__0: { targetNode: NodeType | null; nodes: NodeType[]; event: T; }`
###### Returns
`void`
### NodeTypes
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/nodes.ts#L33)
The `NodeTypes` type is a record that maps node type identifiers to their corresponding Svelte components. This allows you to define custom node types and their implementations.
```ts
type NodeTypes = Record;
```
### Node
### Node\
The `Node` type represents everything Svelte Flow needs to know about a given node.
#### Notes
* You shouldn't try to set the `measured.width` or `measured.height` of a node directly. It is
measured internally by Svelte Flow and used when rendering the node in the
viewport. To control a node's size you should use the `width` and `height`
attributes.
#### Fields
The node data structure that gets used for the nodes prop.
* `id: string` Unique id of a node.
* `position: XYPosition` Position of a node on the pane.
* `data: NodeData` Arbitrary data passed to a node.
* `sourcePosition?: Position` Only relevant for default, source, target nodeType. Controls source position.
* `targetPosition?: Position` Only relevant for default, source, target nodeType. Controls target position.
* `hidden?: boolean` Whether or not the node should be visible on the canvas.
* `selected?: boolean`
* `dragging?: boolean` Whether or not the node is currently being dragged.
* `draggable?: boolean` Whether or not the node is able to be dragged.
* `selectable?: boolean`
* `connectable?: boolean`
* `deletable?: boolean`
* `dragHandle?: string` A class name that can be applied to elements inside the node that allows those elements to act
as drag handles, letting the user drag the node by clicking and dragging on those elements.
* `width?: number`
* `height?: number`
* `initialWidth?: number`
* `initialHeight?: number`
* `parentId?: string` Parent node id, used for creating sub-flows.
* `zIndex?: number`
* `extent?: CoordinateExtent | "parent" | null` Boundary a node can be moved in.
* `expandParent?: boolean` When `true`, the parent node will automatically expand if this node is dragged to the edge of
the parent node's bounds.
* `ariaLabel?: string`
* `origin?: NodeOrigin` Origin of the node relative to its position.
* `handles?: NodeHandle[]`
* `measured?: { width?: number; height?: number; }`
* `type?: string | NodeType | (NodeType & undefined)` Type of node defined in nodeTypes
* `class?: ClassValue`
* `style?: string`
* `focusable?: boolean`
* `ariaRole?: AriaRole | null | undefined` The ARIA role attribute for the node element, used for accessibility.
* `domAttributes?: Omit, "id" | "draggable" | "class" | "style" | "role" | "aria-label" | "dangerouslySetInnerHTML" | keyof DOMAttributes>` General escape hatch for adding custom attributes to the node's DOM element.
### OnBeforeConnect
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/lib/types/general.ts#L31)
The `OnBeforeConnect` type is a callback function that is called before a connection is created. It can prevent the connection or modify it by returning `false` or a modified connection.
```ts
type OnBeforeConnect = (connection: Connection) => Promise;
```
###### Parameters
* `connection: Connection`
###### Returns
`false | void | EdgeType | Connection | null`
### OnBeforeDelete
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L38)
The `OnBeforeDelete` type is a callback function that is called before nodes or edges are deleted. It can prevent deletion or modify the items to be deleted by returning `false` or a modified set of nodes and edges.
```ts
type OnBeforeDelete<
NodeType extends NodeBase = NodeBase,
EdgeType extends EdgeBase = EdgeBase,
> = ({
nodes,
edges,
}: {
nodes: NodeType[];
edges: EdgeType[];
}) => Promise;
```
###### Parameters
* `__0: { nodes: NodeType[]; edges: EdgeType[]; }`
###### Returns
`Promise`
### OnBeforeReconnect
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L34)
The `OnBeforeReconnect` type is a callback function that is called before an edge is
reconnected.
The callback receives the new edge (with the updated connection) and the original edge
being reconnected. You can return the new edge or a modified version of it. Returning
`false`, `null`, or `undefined` aborts the reconnection.
Aborting the reconnection restores the edge to its original state. To delete the edge
instead, call [deleteElements](/api-reference/hooks/use-svelte-flow#deleteelements) to
remove it, then return `null`.
See the [Reconnect Edge](/examples/edges/reconnect-edge) example for more information on
how to implement edge reconnection.
```ts
type OnBeforeReconnect = (
newEdge: EdgeType,
oldEdge: EdgeType,
) => EdgeType | void | false | null;
```
###### Parameters
* `newEdge: EdgeType`
* `oldEdge: EdgeType`
###### Returns
`false | void | EdgeType | null`
### OnConnectEnd
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L89)
The `OnConnectEnd` type represents a callback function that is called when finishing or canceling a connection attempt. It receives the mouse or touch event and the final state of the connection attempt.
```ts
type OnConnectEnd = (
event: MouseEvent | TouchEvent,
connectionState: FinalConnectionState,
) => void;
```
###### Parameters
* `event: MouseEvent | TouchEvent`
* `connectionState: FinalConnectionState`
###### Returns
`void`
### OnConnectStart
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L79)
The `OnConnectStart` type is a callback function that is called when starting to create a connection between nodes. It receives the mouse or touch event and parameters about the connection being started.
```ts
type OnConnectStart = (
event: MouseEvent | TouchEvent,
params: OnConnectStartParams,
) => void;
```
###### Parameters
* `event: MouseEvent | TouchEvent`
* `params: OnConnectStartParams`
###### Returns
`void`
### OnConnect
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L80)
The `OnConnect` type is a callback function that is called when a new connection is created between nodes. It receives a connection object containing the source and target node IDs and their respective handle IDs.
```ts
type OnConnect = (connection: Connection) => void;
```
###### Parameters
* `connection: Connection`
###### Returns
`void`
### OnDelete
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts)
The `OnDelete` type is a callback function that is called when nodes or edges are deleted
from the flow. It receives the deleted nodes and edges.
```ts
type OnDelete<
NodeType extends NodeBase = NodeBase,
EdgeType extends EdgeBase = EdgeBase,
> = ({ nodes, edges }: { nodes: NodeType[]; edges: EdgeType[] }) => void;
```
This type can be used to type the `onDelete` function with a custom node and edge type.
###### Parameters
* `params: { nodes: NodeType[]; edges: EdgeType[]; }`
###### Returns
`void`
### OnError
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L88)
The `OnError` type is a callback function that is called when an error occurs in SvelteFlow. It receives the error ID and a message describing the error.
```ts
type OnError = (id: string, message: string) => void;
```
###### Parameters
* `id: string`
* `message: string`
###### Returns
`void`
### OnMove
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L86)
The `OnMove` type is a callback that fires whenever the viewport is moved, either by user interaction or programmatically. It receives the triggering event and the new viewport state.
```ts
type OnMove = (event: MouseEvent | TouchEvent | null, viewport: Viewport) => void;
```
This type is used to define the `onMove` handler.
###### Parameters
* `event: MouseEvent | TouchEvent`
* `viewport: Viewport`
###### Returns
`void`
### OnReconnectEnd
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L89)
The `OnReconnectEnd` type is a callback that fires when the reconnection process for an edge is completed or canceled. It receives the triggering event, the edge, the handle type, and the final connection state.
```ts
type OnReconnectEnd = (
event: MouseEvent | TouchEvent,
edge: EdgeType,
handleType: HandleType,
connectionState: FinalConnectionState,
) => void;
```
###### Parameters
* `event: MouseEvent | TouchEvent`
* `edge: EdgeType`
* `handleType: 'source' | 'target'`
* `connectionState: FinalConnectionState`
###### Returns
`void`
### OnReconnectStart
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L84)
The `OnReconnectStart` type is a callback function that is called when reconnecting an existing edge. It receives the mouse or touch event, the edge being reconnected, and the type of handle being used.
```ts
type OnReconnectStart = (
event: MouseEvent | TouchEvent,
edge: EdgeType,
handleType: HandleType,
) => void;
```
###### Parameters
* `event: MouseEvent | TouchEvent`
* `edge: EdgeType`
* `handleType: 'source' | 'target'`
###### Returns
`void`
### OnReconnect
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/general.ts#L83)
The `OnReconnect` type is a callback function that is called when an existing edge is reconnected to a different node or handle. It receives the old edge and the new connection details.
```ts
type OnReconnect = (
oldEdge: EdgeType,
newConnection: Connection,
) => void;
```
###### Parameters
* `oldEdge: EdgeType`
* `newConnection: Connection`
###### Returns
`void`
### OnSelectionDrag
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/events.ts#L74)
The `OnSelectionDrag` type is a callback function that is called when dragging a selection of nodes. It receives the mouse event and an array of the nodes being dragged.
```ts
type OnSelectionDrag = (
event: MouseEvent,
nodes: NodeType[],
) => void;
```
###### Parameters
* `event: MouseEvent`
* `nodes: NodeBase, string | undefined>[]`
###### Returns
`void`
### PanOnScrollMode
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#167)
The `PanOnScrollMode` enum controls the panning behavior of the viewport when the user scrolls. Choose `Free` for unrestricted panning, `Vertical` for up-and-down only, or `Horizontal` for left-and-right only.
```ts
enum PanOnScrollMode {
Free = 'free',
Vertical = 'vertical',
Horizontal = 'horizontal',
}
```
### PanelPosition
This type is mostly used to help position things on top of the flow viewport. For
example both the [``](/api-reference/components/mini-map) and
[``](/api-reference/components/controls) components take a `position`
prop of this type.
```ts
export type PanelPosition =
| 'top-left'
| 'top-center'
| 'top-right'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right'
| 'center-left'
| 'center-right';
```
#### Fields
### Position
While [`PanelPosition`](/api-reference/types/panel-position) can be used to place a component in the corners of a container, the `Position` enum is less precise and used primarily in relation to edges and handles.
```ts
export enum Position {
Left = 'left',
Top = 'top',
Right = 'right',
Bottom = 'bottom',
}
```
### Rect
[Source on GitHub](https://github.com/xyflow/xyflow/blob/f0ce2c876d8688e13632bc86286cf857f86dead6/packages/system/src/types/utils.ts/#L39-L40)
The `Rect` type defines a rectangle in a two-dimensional space with dimensions and a
position.
* `width: number`
* `height: number`
* `x: number`
* `y: number`
### SelectionMode
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/types/general.ts#L223)
The `SelectionMode` enum determines how node selection works. Use `Full` to require the entire node to be within the selection area, or `Partial` to allow overlap.
```ts
enum SelectionMode {
Partial = 'partial',
Full = 'full',
}
```
### SnapGrid
The SnapGrid type is used to define the grid size for snapping nodes on the pane.
```ts
type SnapGrid = [number, number];
```
### SvelteFlowStore
The `SvelteFlowStore` type is the structure of the internal Svelte Flow Store, that you can access via the [useStore](/api-reference/hooks/use-store) hook.
The internal Svelte Flow store should only be used for advanced use cases. It's not
recommended to be used directly.
#### Fields
* `flowId: string`
* `domNode: HTMLDivElement | null`
* `panZoom: PanZoomInstance | null`
* `width: number`
* `height: number`
* `zIndexMode: ZIndexMode`
* `nodesInitialized: boolean`
* `viewportInitialized: boolean`
* `_edges: EdgeType[]`
* `nodes: NodeType[]`
* `edges: EdgeType[]`
* `_prevSelectedNodes: NodeType[]`
* `_prevSelectedNodeIds: Set`
* `selectedNodes: NodeType[]`
* `_prevSelectedEdges: EdgeType[]`
* `_prevSelectedEdgeIds: Set`
* `selectedEdges: EdgeType[]`
* `selectionChangeHandlers: Map>`
* `nodeLookup: NodeLookup>`
* `parentLookup: ParentLookup>`
* `connectionLookup: ConnectionLookup`
* `edgeLookup: EdgeLookup`
* `_prevVisibleEdges: Map>`
* `visible: { nodes: Map>; edges: Map>; }`
* `nodesDraggable: boolean`
* `nodesConnectable: boolean`
* `elementsSelectable: boolean`
* `nodesFocusable: boolean`
* `edgesFocusable: boolean`
* `disableKeyboardA11y: boolean`
* `minZoom: number`
* `maxZoom: number`
* `nodeOrigin: NodeOrigin`
* `nodeExtent: CoordinateExtent`
* `translateExtent: CoordinateExtent`
* `defaultEdgeOptions: Partial`
* `nodeDragThreshold: number`
* `autoPanOnNodeDrag: boolean`
* `autoPanOnConnect: boolean`
* `autoPanOnNodeFocus: boolean`
* `autoPanSpeed: number`
* `connectionDragThreshold: number`
* `fitViewQueued: boolean`
* `fitViewOptions: FitViewOptions | undefined`
* `fitViewResolver: any`
* `snapGrid: SnapGrid | null`
* `dragging: boolean`
* `selectionRect: SelectionRect | null`
* `selectionKeyPressed: boolean`
* `multiselectionKeyPressed: boolean`
* `deleteKeyPressed: boolean`
* `panActivationKeyPressed: boolean`
* `zoomActivationKeyPressed: boolean`
* `selectionRectMode: string | null`
* `ariaLiveMessage: string`
* `selectionMode: SelectionMode`
* `nodeTypes: NodeTypes`
* `edgeTypes: EdgeTypes`
* `noPanClass: string`
* `noDragClass: string`
* `noWheelClass: string`
* `ariaLabelConfig: { 'node.a11yDescription.default': string; 'node.a11yDescription.keyboardDisabled': string; 'node.a11yDescription.ariaLiveMessage': ({ direction, x, y }: { direction: string; x: number; y: number; }) => string; ... 7 more ...; 'handle.ariaLabel': string; }`
* `_viewport: Viewport`
* `viewport: Viewport`
* `_connection: ConnectionState`
* `connection: ConnectionState`
* `connectionMode: ConnectionMode`
* `connectionRadius: number`
* `isValidConnection: IsValidConnection`
* `selectNodesOnDrag: boolean`
* `defaultMarkerColor: string | null`
* `markers: MarkerProps[]`
* `onlyRenderVisibleElements: boolean`
* `onerror: OnError`
* `ondelete?: OnDelete`
* `onbeforedelete?: OnBeforeDelete`
* `onbeforeconnect?: OnBeforeConnect`
* `onconnect?: OnConnect`
* `onconnectstart?: OnConnectStart`
* `onconnectend?: OnConnectEnd`
* `onbeforereconnect?: OnBeforeReconnect`
* `onreconnect?: OnReconnect`
* `onreconnectstart?: OnReconnectStart`
* `onreconnectend?: OnReconnectEnd`
* `clickConnect?: boolean`
* `onclickconnectstart?: OnConnectStart`
* `onclickconnectend?: OnConnectEnd`
* `clickConnectStartHandle: Pick | null`
* `onselectiondrag?: OnSelectionDrag`
* `onselectiondragstart?: OnSelectionDrag`
* `onselectiondragstop?: OnSelectionDrag`
* `resolveFitView: () => Promise`
* `_prefersDark: MediaQuery`
* `colorMode: ColorModeClass`
* `resetStoreValues: () => void`
* `setNodeTypes: (nodeTypes: NodeTypes) => void`
* `setEdgeTypes: (edgeTypes: EdgeTypes) => void`
* `addEdge: (edge: EdgeType | Connection) => void`
* `zoomIn: (options?: ViewportHelperFunctionOptions | undefined) => Promise`
* `zoomOut: (options?: ViewportHelperFunctionOptions | undefined) => Promise`
* `setMinZoom: (minZoom: number) => void`
* `setMaxZoom: (maxZoom: number) => void`
* `setTranslateExtent: (extent: CoordinateExtent) => void`
* `fitView: (options?: FitViewOptions | undefined) => Promise`
* `setCenter: (x: number, y: number, options?: ViewportHelperFunctionOptions & { zoom?: number; }) => Promise`
* `updateNodePositions: UpdateNodePositions`
* `updateNodeInternals: (updates: Map) => void`
* `unselectNodesAndEdges: (params?: { nodes?: NodeType[]; edges?: EdgeType[]; } | undefined) => void`
* `addSelectedNodes: (ids: string[]) => void`
* `addSelectedEdges: (ids: string[]) => void`
* `handleNodeSelection: (id: string, unselect?: boolean | undefined, nodeRef?: HTMLDivElement | null | undefined) => void`
* `handleEdgeSelection: (id: string) => void`
* `moveSelectedNodes: (direction: XYPosition, factor: number) => void`
* `panBy: (delta: XYPosition) => Promise`
* `updateConnection: UpdateConnection`
* `cancelConnection: () => void`
* `reset: () => void`
### Viewport
Internally, Svelte Flow maintains a coordinate system that is independent of the
rest of the page. The `Viewport` type tells you where in that system your flow
is currently being display at and how zoomed in or out it is.
#### Fields
Internally, React Flow maintains a coordinate system that is independent of the
rest of the page. The `Viewport` type tells you where in that system your flow
is currently being display at and how zoomed in or out it is.
* `x: number`
* `y: number`
* `zoom: number`
#### Notes
* A `Transform` has the same properties as the viewport, but they represent
different things. Make sure you don't get them muddled up or things will start
to look weird!
### XYPosition
All positions are stored in an object with x and y coordinates.
#### Fields
All positions are stored in an object with x and y coordinates.
* `x: number`
* `y: number`
### ZIndexMode
The ZIndexMode type is used to define how z-indexing is calculated for nodes and edges.
* `auto` mode will automatically manage z-indexing for selections and sub flows.
* `basic` mode will only manage z-indexing for selections.
* `manual` mode does not apply any automatic z-indexing.
```ts
export type ZIndexMode = 'auto' | 'basic' | 'manual';
```
### addEdge()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/general.ts/#L104-L138)
This util is a convenience function to add a new [`Edge`](/api-reference/types/edge) to an
array of edges. It also performs some validation to make sure you don't add an
invalid edge or duplicate an existing one.
```js
import { addEdge } from '@xyflow/svelte';
let edges = $state.raw([]);
const onAddEdge = () => {
const newEdge = {
id: '1-2',
source: '1',
target: '2',
};
edges = addEdge(newEdge, edges.current);
};
```
#### Signature
This util is a convenience function to add a new Edge to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
###### Parameters
* `edgeParams: EdgeType | Connection` Either an `Edge` or a `Connection` you want to add.
* `edges: EdgeType[]` The array of all current edges.
* `options.getEdgeId?: GetEdgeId` Custom function to generate edge IDs. If not provided, the default `getEdgeId` function is used.
###### Returns
`EdgeType[]`
#### Notes
* If an edge with the same `target` and `source` already exists (and the same
`targetHandle` and `sourceHandle` if those are set), then this util won't add
a new edge even if the `id` property is different.
### getBezierPath()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/bezier-edge.ts/#L95)
The `getBezierPath` util returns everything you need to render a bezier edge
between two nodes.
```js
import { Position, getBezierPath } from '@xyflow/svelte';
const source = { x: 0, y: 20 };
const target = { x: 150, y: 100 };
const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
sourceX: source.x,
sourceY: source.y,
sourcePosition: Position.Right,
targetX: target.x,
targetY: target.y,
targetPosition: Position.Left,
});
console.log(path); //=> "M0,20 C75,20 75,100 150,100"
console.log(labelX, labelY); //=> 75, 60
console.log(offsetX, offsetY); //=> 75, 40
```
#### Signature
The `getBezierPath` util returns everything you need to render a bezier edge
between two nodes.
###### Parameters
* `[0].sourceX: number` The `x` position of the source handle.
* `[0].sourceY: number` The `y` position of the source handle.
* `[0].sourcePosition?: Position` The position of the source handle.
* `[0].targetX: number` The `x` position of the target handle.
* `[0].targetY: number` The `y` position of the target handle.
* `[0].targetPosition?: Position` The position of the target handle.
* `[0].curvature?: number` The curvature of the bezier edge.
###### Returns
`[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]`
#### Notes
* This function returns a tuple (aka a fixed-size array) to make it easier to
work with multiple edge paths at once.
### getConnectedEdges()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L327-L337)
Given an array of nodes that may be connected to one another and an array of
*all* your edges, this util gives you an array of edges that connect any of the
given nodes together.
```js
import { getConnectedEdges } from '@xyflow/svelte';
let nodes = $state.raw([]);
let edges = $state.raw([]);
const connectedEdges = getConnectedEdges(nodes.value, edges.value);
```
#### Signature
This utility filters an array of edges, keeping only those where either the source or target
node is present in the given array of nodes.
###### Parameters
* `nodes: NodeType[]` Nodes you want to get the connected edges for.
* `edges: EdgeType[]` All edges.
###### Returns
`EdgeType[]`
### getIncomers()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L121-L137)
This util is used to tell you what nodes, if any, are connected to the given node
as the *source* of an edge.
```ts
import { getIncomers } from '@xyflow/svelte';
let nodes = $state.raw([]);
let edges = $state.raw([]);
const incomers = getIncomers(
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
nodes.value,
edges.value,
);
```
#### Signature
This util is used to tell you what nodes, if any, are connected to the given node
as the *source* of an edge.
###### Parameters
* `node: NodeType | { id: string; }` The node to get the connected nodes from.
* `nodes: NodeType[]` The array of all nodes.
* `edges: EdgeType[]` The array of all edges.
###### Returns
`NodeType[]`
### getNodesBounds()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L195-L229)
Returns the bounding box that contains all the given nodes in an array. This can
be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds)
to calculate the correct transform to fit the given nodes in a viewport.
This function was previously called `getRectOfNodes`, which will be removed in v12.
```js
import { getNodesBounds } from '@xyflow/svelte';
let nodes = $state.raw([
{
id: 'a',
position: { x: 0, y: 0 },
data: { label: 'a' },
width: 50,
height: 25,
},
{
id: 'b',
position: { x: 100, y: 100 },
data: { label: 'b' },
width: 50,
height: 25,
},
]);
const bounds = getNodesBounds(nodes.value);
```
#### Signature
Returns the bounding box that contains all the given nodes in an array. This can
be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds)
to calculate the correct transform to fit the given nodes in a viewport.
###### Parameters
* `nodes: (string | NodeType | InternalNodeBase)[]` Nodes to calculate the bounds for.
* `params.nodeOrigin?: NodeOrigin` Origin of the nodes: `[0, 0]` for top-left, `[0.5, 0.5]` for center.
* `params.nodeLookup?: NodeLookup>`
###### Returns
`Rect`
### getOutgoers()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/graph.ts/#L79-L80)
This util is used to tell you what nodes, if any, are connected to the given node
as the *target* of an edge.
```ts
import { getOutgoers } from '@xyflow/svelte';
let nodes = $state.raw([]);
let edges = $state.raw([]);
const incomers = getOutgoers(
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
nodes.value,
edges.value,
);
```
#### Signature
This util is used to tell you what nodes, if any, are connected to the given node
as the *target* of an edge.
###### Parameters
* `node: NodeType | { id: string; }` The node to get the connected nodes from.
* `nodes: NodeType[]` The array of all nodes.
* `edges: EdgeType[]` The array of all edges.
###### Returns
`NodeType[]`
### getSmoothStepPath()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/smoothstep-edge.ts/#L244-L245)
The `getSmoothStepPath` util returns everything you need to render a stepped path
between two nodes. The `borderRadius` property can be used to choose how rounded
the corners of those steps are.
```js
import { Position, getSmoothStepPath } from '@xyflow/svelte';
const source = { x: 0, y: 20 };
const target = { x: 150, y: 100 };
const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
sourceX: source.x,
sourceY: source.y,
sourcePosition: Position.Right,
targetX: target.x,
targetY: target.y,
targetPosition: Position.Left,
});
console.log(path); //=> "M0 20L20 20L 70,20Q 75,20 75,25L 75,95Q ..."
console.log(labelX, labelY); //=> 75, 60
console.log(offsetX, offsetY); //=> 75, 40
```
#### Signature
The `getSmoothStepPath` util returns everything you need to render a stepped path
between two nodes. The `borderRadius` property can be used to choose how rounded
the corners of those steps are.
###### Parameters
* `[0].sourceX: number` The `x` position of the source handle.
* `[0].sourceY: number` The `y` position of the source handle.
* `[0].sourcePosition?: Position` The position of the source handle.
* `[0].targetX: number` The `x` position of the target handle.
* `[0].targetY: number` The `y` position of the target handle.
* `[0].targetPosition?: Position` The position of the target handle.
* `[0].borderRadius?: number`
* `[0].centerX?: number`
* `[0].centerY?: number`
* `[0].offset?: number`
* `[0].stepPosition?: number` Controls where the bend occurs along the path.
0 = at source, 1 = at target, 0.5 = midpoint
###### Returns
`[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]`
#### Notes
* This function returns a tuple (aka a fixed-size array) to make it easier to
work with multiple edge paths at once.
* You can set the `borderRadius` property to `0` to get a step edge path.
### getStraightPath()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/edges/straight-edge.ts/#L43-L44)
Calculates the straight line path between two points.
```js
import { getStraightPath } from '@xyflow/svelte';
const source = { x: 0, y: 20 };
const target = { x: 150, y: 100 };
const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
sourceX: source.x,
sourceY: source.y,
targetX: target.x,
targetY: target.y,
});
console.log(path); //=> "M 0,20L 150,100"
console.log(labelX, labelY); //=> 75, 60
console.log(offsetX, offsetY); //=> 75, 40
```
#### Signature
Calculates the straight line path between two points.
###### Parameters
* `[0].sourceX: number` The `x` position of the source handle.
* `[0].sourceY: number` The `y` position of the source handle.
* `[0].targetX: number` The `x` position of the target handle.
* `[0].targetY: number` The `y` position of the target handle.
###### Returns
`[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]`
#### Notes
* This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
### getViewportForBounds()
[Source on Github](https://github.com/xyflow/xyflow/blob/main/packages/system/src/utils/general.ts/#L293-L294)
This util returns the viewport for the given bounds. You might use this to pre-calculate
the viewport for a given set of nodes on the server or calculate the viewport for the
given bounds *without* changing the viewport directly.
```js
import { getViewportForBounds } from '@xyflow/svelte';
const { x, y, zoom } = getViewportForBounds(
{
x: 0,
y: 0,
width: 100,
height: 100,
},
1200,
800,
0.5,
2,
);
```
#### Signature
Returns a viewport that encloses the given bounds with padding.
###### Parameters
* `bounds: Rect` Bounds to fit inside viewport.
* `width: number` Width of the viewport.
* `height: number` Height of the viewport.
* `minZoom: number` Minimum zoom level of the resulting viewport.
* `maxZoom: number` Maximum zoom level of the resulting viewport.
* `padding: Padding` Padding around the bounds.
###### Returns
* `x: number`
* `y: number`
* `zoom: number`
#### Notes
* This is quite a low-level utility. You might want to look at the
[`fitView`](/api-reference/hooks/use-svelte-flow#fitview) or
[`fitBounds`](/api-reference/hooks/use-svelte-flow#fitbounds) methods for a more
practical api.
### Utils
### isEdge()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/v11/packages/core/src/utils/graph.ts/#L19)
Test whether an object is usable as an [`Edge`](/api-reference/types/edge). In TypeScript
this is a type guard that will narrow the type of whatever you pass in to
[`Edge`](/api-reference/types/edge) if it returns `true`.
```js
import { isEdge } from '@xyflow/svelte';
const edge = {
id: 'edge-a',
source: 'a',
target: 'b',
};
if (isEdge(edge)) {
// ..
}
```
#### Signature
Test whether an object is usable as an Edge
###### Parameters
* `element: unknown` The element to test
###### Returns
`boolean`
### isNode()
[Source on GitHub](https://github.com/xyflow/xyflow/blob/v11/packages/core/src/utils/graph.ts/#L22)
Test whether an object is usable as an [`Node`](/api-reference/types/node). In TypeScript
this is a type guard that will narrow the type of whatever you pass in to
[`Node`](/api-reference/types/node) if it returns `true`.
```js
import { isNode } from '@xyflow/svelte';
const node = {
id: 'node-a',
data: {
label: 'node',
},
position: {
x: 0,
y: 0,
},
};
if (isNode(node)) {
// ..
}
```
#### Signature
Test whether an object is usable as a Node
###### Parameters
* `element: unknown` The element to test
###### Returns
`boolean`