svelte-flow

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Svelte Flow

Svelte Flow

Expert guide for building node-based UIs with Svelte Flow (@xyflow/svelte).
基于Svelte Flow (@xyflow/svelte) 构建节点式UI的专家指南。

Installation

安装

bash
npm install @xyflow/svelte
bash
npm install @xyflow/svelte

Core Setup

核心设置

svelte
<script lang="ts">
  import { SvelteFlow, Background } from '@xyflow/svelte';
  import '@xyflow/svelte/dist/style.css';
  
  // Use $state.raw for nodes and edges (Svelte 5)
  let nodes = $state.raw([
    { id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 } },
    { id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 100 } }
  ]);
  
  let edges = $state.raw([
    { id: 'e1-2', source: '1', target: '2' }
  ]);
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Background />
</SvelteFlow>
svelte
<script lang="ts">
  import { SvelteFlow, Background } from '@xyflow/svelte';
  import '@xyflow/svelte/dist/style.css';
  
  // Use $state.raw for nodes and edges (Svelte 5)
  let nodes = $state.raw([
    { id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 } },
    { id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 100 } }
  ]);
  
  let edges = $state.raw([
    { id: 'e1-2', source: '1', target: '2' }
  ]);
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Background />
</SvelteFlow>

Key Concepts

核心概念

Nodes

节点

Each node requires:
  • id
    : Unique identifier
  • position
    :
    { x: number, y: number }
  • data
    : Object with custom properties (typically includes
    label
    )
  • Optional:
    type
    ,
    style
    ,
    hidden
    ,
    width
    ,
    height
    ,
    sourcePosition
    ,
    targetPosition
typescript
const node = {
  id: '1',
  type: 'custom',
  position: { x: 100, y: 100 },
  data: { label: 'My Node', color: '#ff0000' },
  style: 'background: #f0f0f0',
  width: 150,
  height: 100
};
每个节点需要包含以下属性:
  • id
    :唯一标识符
  • position
    { x: number, y: number }
    (坐标位置)
  • data
    :包含自定义属性的对象(通常包含
    label
    标签)
  • 可选属性:
    type
    (类型)、
    style
    (样式)、
    hidden
    (隐藏状态)、
    width
    (宽度)、
    height
    (高度)、
    sourcePosition
    (源连接点位置)、
    targetPosition
    (目标连接点位置)
typescript
const node = {
  id: '1',
  type: 'custom',
  position: { x: 100, y: 100 },
  data: { label: 'My Node', color: '#ff0000' },
  style: 'background: #f0f0f0',
  width: 150,
  height: 100
};

Edges

Each edge requires:
  • id
    : Unique identifier
  • source
    : Source node ID
  • target
    : Target node ID
  • Optional:
    type
    ,
    label
    ,
    animated
    ,
    markerEnd
    ,
    markerStart
    ,
    style
    ,
    hidden
typescript
const edge = {
  id: 'e1-2',
  source: '1',
  target: '2',
  type: 'smoothstep',
  label: 'connects to',
  animated: true,
  markerEnd: { type: MarkerType.ArrowClosed }
};
每条边需要包含以下属性:
  • id
    :唯一标识符
  • source
    :源节点ID
  • target
    :目标节点ID
  • 可选属性:
    type
    (类型)、
    label
    (标签)、
    animated
    (动画效果)、
    markerEnd
    (末端标记)、
    markerStart
    (起始标记)、
    style
    (样式)、
    hidden
    (隐藏状态)
typescript
const edge = {
  id: 'e1-2',
  source: '1',
  target: '2',
  type: 'smoothstep',
  label: 'connects to',
  animated: true,
  markerEnd: { type: MarkerType.ArrowClosed }
};

Built-in Types

内置类型

Edge Types:
  • default
    : Straight line
  • smoothstep
    : Smooth 90-degree turns
  • step
    : Hard 90-degree turns
  • straight
    : Alias for default
  • bezier
    : Curved bezier line
Node Types:
  • default
    : Basic rectangle node
  • input
    : Node with only source handles
  • output
    : Node with only target handles
边类型:
  • default
    :直线
  • smoothstep
    :平滑90度转折
  • step
    :直角90度转折
  • straight
    default
    的别名
  • bezier
    :贝塞尔曲线
节点类型:
  • default
    :基础矩形节点
  • input
    :仅包含源连接点的节点
  • output
    :仅包含目标连接点的节点

Custom Nodes

自定义节点

svelte
<!-- CustomNode.svelte -->
<script lang="ts" module>
  import type { Node, NodeProps } from '@xyflow/svelte';
  
  export type CustomNodeType = Node<{
    label: string;
    color?: string;
  }>;
</script>

<script lang="ts">
  import { Handle, Position } from '@xyflow/svelte';
  
  let { data }: NodeProps<CustomNodeType> = $props();
</script>

<div class="custom-node" style:background={data.color}>
  <Handle type="target" position={Position.Left} />
  <div>{data.label}</div>
  <Handle type="source" position={Position.Right} />
</div>

<style>
  .custom-node {
    padding: 10px;
    border-radius: 5px;
    border: 1px solid #ddd;
  }
</style>
Register custom nodes:
svelte
<script lang="ts">
  import CustomNode from './CustomNode.svelte';
  
  const nodeTypes = {
    custom: CustomNode
  };
  
  let nodes = $state.raw([
    { id: '1', type: 'custom', data: { label: 'Custom', color: '#ff7000' }, position: { x: 0, y: 0 } }
  ]);
</script>

<SvelteFlow bind:nodes {nodeTypes} bind:edges fitView />
svelte
<!-- CustomNode.svelte -->
<script lang="ts" module>
  import type { Node, NodeProps } from '@xyflow/svelte';
  
  export type CustomNodeType = Node<{
    label: string;
    color?: string;
  }>;
</script>

<script lang="ts">
  import { Handle, Position } from '@xyflow/svelte';
  
  let { data }: NodeProps<CustomNodeType> = $props();
</script>

<div class="custom-node" style:background={data.color}>
  <Handle type="target" position={Position.Left} />
  <div>{data.label}</div>
  <Handle type="source" position={Position.Right} />
</div>

<style>
  .custom-node {
    padding: 10px;
    border-radius: 5px;
    border: 1px solid #ddd;
  }
</style>
注册自定义节点:
svelte
<script lang="ts">
  import CustomNode from './CustomNode.svelte';
  
  const nodeTypes = {
    custom: CustomNode
  };
  
  let nodes = $state.raw([
    { id: '1', type: 'custom', data: { label: 'Custom', color: '#ff7000' }, position: { x: 0, y: 0 } }
  ]);
</script>

<SvelteFlow bind:nodes {nodeTypes} bind:edges fitView />

Custom Edges

自定义边

svelte
<!-- CustomEdge.svelte -->
<script lang="ts">
  import { BaseEdge, EdgeLabel, getStraightPath, type EdgeProps } from '@xyflow/svelte';
  
  let { id, sourceX, sourceY, targetX, targetY, label }: EdgeProps = $props();
  
  let [edgePath, labelX, labelY] = $derived(
    getStraightPath({ sourceX, sourceY, targetX, targetY })
  );
</script>

<BaseEdge {id} path={edgePath} />
{#if label}
  <EdgeLabel x={labelX} y={labelY}>
    <div class="edge-label">{label}</div>
  </EdgeLabel>
{/if}
Path helpers available:
  • getStraightPath()
  • getBezierPath()
  • getSmoothStepPath()
Register custom edges:
svelte
<script lang="ts">
  const edgeTypes = {
    custom: CustomEdge
  };
</script>

<SvelteFlow bind:nodes bind:edges {edgeTypes} fitView />
svelte
<!-- CustomEdge.svelte -->
<script lang="ts">
  import { BaseEdge, EdgeLabel, getStraightPath, type EdgeProps } from '@xyflow/svelte';
  
  let { id, sourceX, sourceY, targetX, targetY, label }: EdgeProps = $props();
  
  let [edgePath, labelX, labelY] = $derived(
    getStraightPath({ sourceX, sourceY, targetX, targetY })
  );
</script>

<BaseEdge {id} path={edgePath} />
{#if label}
  <EdgeLabel x={labelX} y={labelY}>
    <div class="edge-label">{label}</div>
  </EdgeLabel>
{/if}
可用路径辅助函数:
  • getStraightPath()
  • getBezierPath()
  • getSmoothStepPath()
注册自定义边:
svelte
<script lang="ts">
  const edgeTypes = {
    custom: CustomEdge
  };
</script>

<SvelteFlow bind:nodes bind:edges {edgeTypes} fitView />

Updating State

更新状态

Critical: Nodes and edges are immutable. Create new objects to trigger updates.
svelte
<script lang="ts">
  // ❌ Won't work - direct mutation
  nodes[0].position.x = 100;
  
  // ✅ Works - create new array
  nodes = nodes.map((node) => {
    if (node.id === '1') {
      return { ...node, position: { ...node.position, x: 100 } };
    }
    return node;
  });
  
  // ✅ Also works - using helper from useSvelteFlow
  import { useSvelteFlow } from '@xyflow/svelte';
  const { updateNode } = useSvelteFlow();
  
  updateNode('1', (node) => ({
    ...node,
    position: { ...node.position, x: 100 }
  }));
</script>
重要提示: 节点和边是不可变的,需要创建新对象来触发更新。
svelte
<script lang="ts">
  // ❌ 无效 - 直接修改
  nodes[0].position.x = 100;
  
  // ✅ 有效 - 创建新数组
  nodes = nodes.map((node) => {
    if (node.id === '1') {
      return { ...node, position: { ...node.position, x: 100 } };
    }
    return node;
  });
  
  // ✅ 同样有效 - 使用useSvelteFlow提供的辅助函数
  import { useSvelteFlow } from '@xyflow/svelte';
  const { updateNode } = useSvelteFlow();
  
  updateNode('1', (node) => ({
    ...node,
    position: { ...node.position, x: 100 }
  }));
</script>

Event Handling

事件处理

Common event handlers:
svelte
<SvelteFlow
  bind:nodes
  bind:edges
  onnodeclick={(event) => console.log('node clicked', event.targetNode)}
  onnodedrag={(event) => console.log('node dragging', event.targetNode)}
  onnodedragstop={(event) => console.log('drag ended', event.targetNode)}
  onedgeclick={(event) => console.log('edge clicked', event.edge)}
  onconnect={(params) => {
    edges = [...edges, { id: `e${params.source}-${params.target}`, ...params }];
  }}
/>
Available events:
  • onnodeclick
    ,
    onnodedoubleclick
  • onnodedrag
    ,
    onnodedragstart
    ,
    onnodedragstop
  • onedgeclick
    ,
    onedgedoubleclick
  • onconnect
    ,
    onconnectstart
    ,
    onconnectend
  • onbeforedelete
    (async, can prevent deletion)
  • oninit
    ,
    onmove
    ,
    onmovestart
    ,
    onmoveend
常用事件处理器:
svelte
<SvelteFlow
  bind:nodes
  bind:edges
  onnodeclick={(event) => console.log('node clicked', event.targetNode)}
  onnodedrag={(event) => console.log('node dragging', event.targetNode)}
  onnodedragstop={(event) => console.log('drag ended', event.targetNode)}
  onedgeclick={(event) => console.log('edge clicked', event.edge)}
  onconnect={(params) => {
    edges = [...edges, { id: `e${params.source}-${params.target}`, ...params }];
  }}
/>
可用事件:
  • onnodeclick
    (节点点击)、
    onnodedoubleclick
    (节点双击)
  • onnodedrag
    (节点拖动中)、
    onnodedragstart
    (节点开始拖动)、
    onnodedragstop
    (节点停止拖动)
  • onedgeclick
    (边点击)、
    onedgedoubleclick
    (边双击)
  • onconnect
    (连接创建)、
    onconnectstart
    (开始连接)、
    onconnectend
    (结束连接)
  • onbeforedelete
    (删除前触发,异步,可阻止删除)
  • oninit
    (初始化完成)、
    onmove
    (画布移动中)、
    onmovestart
    (画布开始移动)、
    onmoveend
    (画布停止移动)

Layout Algorithms

布局算法

Dagre (Hierarchical)

Dagre(层级布局)

typescript
import dagre from '@dagrejs/dagre';
import { Position } from '@xyflow/svelte';

function getLayoutedElements(nodes: Node[], edges: Edge[], direction = 'TB') {
  const dagreGraph = new dagre.graphlib.Graph();
  dagreGraph.setDefaultEdgeLabel(() => ({}));
  
  const isHorizontal = direction === 'LR';
  dagreGraph.setGraph({ rankdir: direction });
  
  const nodeWidth = 172;
  const nodeHeight = 36;
  
  nodes.forEach((node) => {
    dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
  });
  
  edges.forEach((edge) => {
    dagreGraph.setEdge(edge.source, edge.target);
  });
  
  dagre.layout(dagreGraph);
  
  const layoutedNodes = nodes.map((node) => {
    const nodeWithPosition = dagreGraph.node(node.id);
    return {
      ...node,
      targetPosition: isHorizontal ? Position.Left : Position.Top,
      sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
      position: {
        x: nodeWithPosition.x - nodeWidth / 2,
        y: nodeWithPosition.y - nodeHeight / 2,
      },
    };
  });
  
  return { nodes: layoutedNodes, edges };
}
typescript
import dagre from '@dagrejs/dagre';
import { Position } from '@xyflow/svelte';

function getLayoutedElements(nodes: Node[], edges: Edge[], direction = 'TB') {
  const dagreGraph = new dagre.graphlib.Graph();
  dagreGraph.setDefaultEdgeLabel(() => ({}));
  
  const isHorizontal = direction === 'LR';
  dagreGraph.setGraph({ rankdir: direction });
  
  const nodeWidth = 172;
  const nodeHeight = 36;
  
  nodes.forEach((node) => {
    dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
  });
  
  edges.forEach((edge) => {
    dagreGraph.setEdge(edge.source, edge.target);
  });
  
  dagre.layout(dagreGraph);
  
  const layoutedNodes = nodes.map((node) => {
    const nodeWithPosition = dagreGraph.node(node.id);
    return {
      ...node,
      targetPosition: isHorizontal ? Position.Left : Position.Top,
      sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
      position: {
        x: nodeWithPosition.x - nodeWidth / 2,
        y: nodeWithPosition.y - nodeHeight / 2,
      },
    };
  });
  
  return { nodes: layoutedNodes, edges };
}

Advanced Features

高级功能

Floating Edges

浮动边

Edges that dynamically connect to the closest point on node boundaries:
svelte
<!-- FloatingEdge.svelte -->
<script lang="ts">
  import { BaseEdge, getBezierPath, type EdgeProps } from '@xyflow/svelte';
  import { getEdgeParams } from './utils';
  
  let { id, source, target, markerEnd }: EdgeProps = $props();
  
  const { sx, sy, tx, ty, sourcePos, targetPos } = $derived(
    getEdgeParams(source, target)
  );
  
  let [edgePath] = $derived(
    getBezierPath({
      sourceX: sx,
      sourceY: sy,
      sourcePosition: sourcePos,
      targetX: tx,
      targetY: ty,
      targetPosition: targetPos,
    })
  );
</script>

<BaseEdge {id} path={edgePath} {markerEnd} />
Register with
ConnectionMode.Loose
:
svelte
<script lang="ts">
  import { ConnectionMode } from '@xyflow/svelte';
</script>

<SvelteFlow
  bind:nodes
  bind:edges
  edgeTypes={{ floating: FloatingEdge }}
  connectionMode={ConnectionMode.Loose}
  fitView
/>
可动态连接到节点边界最近点的边:
svelte
<!-- FloatingEdge.svelte -->
<script lang="ts">
  import { BaseEdge, getBezierPath, type EdgeProps } from '@xyflow/svelte';
  import { getEdgeParams } from './utils';
  
  let { id, source, target, markerEnd }: EdgeProps = $props();
  
  const { sx, sy, tx, ty, sourcePos, targetPos } = $derived(
    getEdgeParams(source, target)
  );
  
  let [edgePath] = $derived(
    getBezierPath({
      sourceX: sx,
      sourceY: sy,
      sourcePosition: sourcePos,
      targetX: tx,
      targetY: ty,
      targetPosition: targetPos,
    })
  );
</script>

<BaseEdge {id} path={edgePath} {markerEnd} />
配合
ConnectionMode.Loose
注册使用:
svelte
<script lang="ts">
  import { ConnectionMode } from '@xyflow/svelte';
</script>

<SvelteFlow
  bind:nodes
  bind:edges
  edgeTypes={{ floating: FloatingEdge }}
  connectionMode={ConnectionMode.Loose}
  fitView
/>

Proximity Connect

邻近连接

Auto-connect nodes when dragged close together:
svelte
<script lang="ts">
  const MIN_DISTANCE = 150;
  
  function getClosestEdge(node: Node, nodes: Node[]) {
    const closestNode = nodes.reduce(
      (res, n) => {
        if (n.id !== node.id) {
          const dx = n.position.x - node.position.x;
          const dy = n.position.y - node.position.y;
          const d = Math.sqrt(dx * dx + dy * dy);
          
          if (d < res.distance && d < MIN_DISTANCE) {
            res.distance = d;
            res.node = n;
          }
        }
        return res;
      },
      { distance: Number.MAX_VALUE, node: null }
    );
    
    if (!closestNode.node) return null;
    
    const closeNodeIsSource = closestNode.node.position.x < node.position.x;
    
    return {
      id: closeNodeIsSource
        ? `${node.id}-${closestNode.node.id}`
        : `${closestNode.node.id}-${node.id}`,
      source: closeNodeIsSource ? closestNode.node.id : node.id,
      target: closeNodeIsSource ? node.id : closestNode.node.id,
      class: 'temp',
    };
  }
  
  function onNodeDrag({ targetNode: node }) {
    const closestEdge = getClosestEdge(node, nodes);
    
    edges = edges.filter(e => e.class !== 'temp');
    
    if (closestEdge && !edges.some(e => e.source === closestEdge.source && e.target === closestEdge.target)) {
      edges = [...edges, closestEdge];
    }
  }
  
  function onNodeDragStop() {
    edges = edges.map((edge) => 
      edge.class === 'temp' ? { ...edge, class: '' } : edge
    );
  }
</script>

<SvelteFlow
  bind:nodes
  bind:edges
  onnodedrag={onNodeDrag}
  onnodedragstop={onNodeDragStop}
  fitView
/>
当节点拖动到彼此附近时自动创建连接:
svelte
<script lang="ts">
  const MIN_DISTANCE = 150;
  
  function getClosestEdge(node: Node, nodes: Node[]) {
    const closestNode = nodes.reduce(
      (res, n) => {
        if (n.id !== node.id) {
          const dx = n.position.x - node.position.x;
          const dy = n.position.y - node.position.y;
          const d = Math.sqrt(dx * dx + dy * dy);
          
          if (d < res.distance && d < MIN_DISTANCE) {
            res.distance = d;
            res.node = n;
          }
        }
        return res;
      },
      { distance: Number.MAX_VALUE, node: null }
    );
    
    if (!closestNode.node) return null;
    
    const closeNodeIsSource = closestNode.node.position.x < node.position.x;
    
    return {
      id: closeNodeIsSource
        ? `${node.id}-${closestNode.node.id}`
        : `${closestNode.node.id}-${node.id}`,
      source: closeNodeIsSource ? closestNode.node.id : node.id,
      target: closeNodeIsSource ? node.id : closestNode.node.id,
      class: 'temp',
    };
  }
  
  function onNodeDrag({ targetNode: node }) {
    const closestEdge = getClosestEdge(node, nodes);
    
    edges = edges.filter(e => e.class !== 'temp');
    
    if (closestEdge && !edges.some(e => e.source === closestEdge.source && e.target === closestEdge.target)) {
      edges = [...edges, closestEdge];
    }
  }
  
  function onNodeDragStop() {
    edges = edges.map((edge) => 
      edge.class === 'temp' ? { ...edge, class: '' } : edge
    );
  }
</script>

<SvelteFlow
  bind:nodes
  bind:edges
  onnodedrag={onNodeDrag}
  onnodedragstop={onNodeDragStop}
  fitView
/>

Edge Reconnection

边重连

Allow users to drag edge endpoints to different nodes:
svelte
<!-- ReconnectableEdge.svelte -->
<script lang="ts">
  import {
    BaseEdge,
    EdgeReconnectAnchor,
    getBezierPath,
    type EdgeProps
  } from '@xyflow/svelte';
  
  let { sourceX, sourceY, targetX, targetY, selected, ...props }: EdgeProps = $props();
  
  const [edgePath] = $derived(
    getBezierPath({ sourceX, sourceY, targetX, targetY })
  );
  
  let reconnecting = $state(false);
</script>

{#if !reconnecting}
  <BaseEdge path={edgePath} {...props} />
{/if}

{#if selected}
  <EdgeReconnectAnchor
    bind:reconnecting
    type="source"
    position={{ x: sourceX, y: sourceY }}
  />
  <EdgeReconnectAnchor
    bind:reconnecting
    type="target"
    position={{ x: targetX, y: targetY }}
  />
{/if}
允许用户拖动边的端点连接到其他节点:
svelte
<!-- ReconnectableEdge.svelte -->
<script lang="ts">
  import {
    BaseEdge,
    EdgeReconnectAnchor,
    getBezierPath,
    type EdgeProps
  } from '@xyflow/svelte';
  
  let { sourceX, sourceY, targetX, targetY, selected, ...props }: EdgeProps = $props();
  
  const [edgePath] = $derived(
    getBezierPath({ sourceX, sourceY, targetX, targetY })
  );
  
  let reconnecting = $state(false);
</script>

{#if !reconnecting}
  <BaseEdge path={edgePath} {...props} />
{/if}

{#if selected}
  <EdgeReconnectAnchor
    bind:reconnecting
    type="source"
    position={{ x: sourceX, y: sourceY }}
  />
  <EdgeReconnectAnchor
    bind:reconnecting
    type="target"
    position={{ x: targetX, y: targetY }}
  />
{/if}

Delete Node and Reconnect Edges

删除节点并重连边

svelte
<script lang="ts">
  import { getIncomers, getOutgoers, getConnectedEdges, type OnBeforeDelete } from '@xyflow/svelte';
  
  const onbeforedelete: OnBeforeDelete = async ({ nodes: deletedNodes }) => {
    let remainingNodes = [...nodes];
    
    edges = deletedNodes.reduce((acc, node) => {
      const incomers = getIncomers(node, remainingNodes, acc);
      const outgoers = getOutgoers(node, remainingNodes, acc);
      const connectedEdges = getConnectedEdges([node], acc);
      
      const remainingEdges = acc.filter((edge) => !connectedEdges.includes(edge));
      
      const createdEdges = incomers.flatMap(({ id: source }) =>
        outgoers.map(({ id: target }) => ({
          id: `${source}->${target}`,
          source,
          target,
        }))
      );
      
      remainingNodes = remainingNodes.filter((rn) => rn.id !== node.id);
      
      return [...remainingEdges, ...createdEdges];
    }, edges);
    
    nodes = remainingNodes;
    
    return true;
  };
</script>

<SvelteFlow bind:nodes bind:edges {onbeforedelete} fitView />
svelte
<script lang="ts">
  import { getIncomers, getOutgoers, getConnectedEdges, type OnBeforeDelete } from '@xyflow/svelte';
  
  const onbeforedelete: OnBeforeDelete = async ({ nodes: deletedNodes }) => {
    let remainingNodes = [...nodes];
    
    edges = deletedNodes.reduce((acc, node) => {
      const incomers = getIncomers(node, remainingNodes, acc);
      const outgoers = getOutgoers(node, remainingNodes, acc);
      const connectedEdges = getConnectedEdges([node], acc);
      
      const remainingEdges = acc.filter((edge) => !connectedEdges.includes(edge));
      
      const createdEdges = incomers.flatMap(({ id: source }) =>
        outgoers.map(({ id: target }) => ({
          id: `${source}->${target}`,
          source,
          target,
        }))
      );
      
      remainingNodes = remainingNodes.filter((rn) => rn.id !== node.id);
      
      return [...remainingEdges, ...createdEdges];
    }, edges);
    
    nodes = remainingNodes;
    
    return true;
  };
</script>

<SvelteFlow bind:nodes bind:edges {onbeforedelete} fitView />

Components

组件

Controls

控制面板

svelte
<script lang="ts">
  import { SvelteFlow, Controls } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Controls showLock={false} showFitView={true} showZoom={true} />
</SvelteFlow>
svelte
<script lang="ts">
  import { SvelteFlow, Controls } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Controls showLock={false} showFitView={true} showZoom={true} />
</SvelteFlow>

MiniMap

迷你地图

svelte
<script lang="ts">
  import { SvelteFlow, MiniMap } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <MiniMap />
</SvelteFlow>
svelte
<script lang="ts">
  import { SvelteFlow, MiniMap } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <MiniMap />
</SvelteFlow>

Background

背景

svelte
<script lang="ts">
  import { SvelteFlow, Background, BackgroundVariant } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Background variant={BackgroundVariant.Dots} gap={12} size={1} />
</SvelteFlow>
Variants:
Dots
,
Lines
,
Cross
svelte
<script lang="ts">
  import { SvelteFlow, Background, BackgroundVariant } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Background variant={BackgroundVariant.Dots} gap={12} size={1} />
</SvelteFlow>
背景样式选项:
Dots
(点状)、
Lines
(线状)、
Cross
(十字线)

Panel (Custom Controls)

面板(自定义控件)

svelte
<script lang="ts">
  import { SvelteFlow, Panel } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Panel position="top-left">
    <button>Custom Button</button>
  </Panel>
</SvelteFlow>
Positions:
top-left
,
top-center
,
top-right
,
bottom-left
,
bottom-center
,
bottom-right
svelte
<script lang="ts">
  import { SvelteFlow, Panel } from '@xyflow/svelte';
</script>

<SvelteFlow bind:nodes bind:edges fitView>
  <Panel position="top-left">
    <button>自定义按钮</button>
  </Panel>
</SvelteFlow>
面板位置选项:
top-left
(左上角)、
top-center
(顶部居中)、
top-right
(右上角)、
bottom-left
(左下角)、
bottom-center
(底部居中)、
bottom-right
(右下角)

Utility Functions

工具函数

typescript
import {
  getConnectedEdges,
  getIncomers,
  getOutgoers,
  addEdge,
  applyEdgeChanges,
  applyNodeChanges
} from '@xyflow/svelte';

// Get edges connected to nodes
const connectedEdges = getConnectedEdges(nodes, edges);

// Get nodes that connect TO a node
const incomers = getIncomers(targetNode, nodes, edges);

// Get nodes that a node connects TO
const outgoers = getOutgoers(sourceNode, nodes, edges);

// Add edge with validation
edges = addEdge(connection, edges);
typescript
import {
  getConnectedEdges,
  getIncomers,
  getOutgoers,
  addEdge,
  applyEdgeChanges,
  applyNodeChanges
} from '@xyflow/svelte';

// 获取与节点关联的边
const connectedEdges = getConnectedEdges(nodes, edges);

// 获取连接到目标节点的所有节点
const incomers = getIncomers(targetNode, nodes, edges);

// 获取目标节点连接到的所有节点
const outgoers = getOutgoers(sourceNode, nodes, edges);

// 带验证的添加边操作
edges = addEdge(connection, edges);

Store Management (Alternative)

状态管理(替代方案)

For centralized state:
javascript
// 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;
svelte
<!-- Component.svelte -->
<script>
  import { getNodes, getEdges, setNodes, setEdges } from './store.svelte.js';
</script>

<SvelteFlow bind:nodes={getNodes, setNodes} bind:edges={getEdges, setEdges} />
适用于集中式状态管理场景:
javascript
// 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;
svelte
<!-- Component.svelte -->
<script>
  import { getNodes, getEdges, setNodes, setEdges } from './store.svelte.js';
</script>

<SvelteFlow bind:nodes={getNodes, setNodes} bind:edges={getEdges, setEdges} />

Export to Image

导出为图片

svelte
<script lang="ts">
  import { useSvelteFlow } from '@xyflow/svelte';
  
  const { toObject, getViewport } = useSvelteFlow();
  
  async function downloadImage() {
    const { toPng } = await import('html-to-image');
    const dataUrl = await toPng(document.querySelector('.svelte-flow'));
    const a = document.createElement('a');
    a.setAttribute('download', 'flow.png');
    a.setAttribute('href', dataUrl);
    a.click();
  }
</script>
svelte
<script lang="ts">
  import { useSvelteFlow } from '@xyflow/svelte';
  
  const { toObject, getViewport } = useSvelteFlow();
  
  async function downloadImage() {
    const { toPng } = await import('html-to-image');
    const dataUrl = await toPng(document.querySelector('.svelte-flow'));
    const a = document.createElement('a');
    a.setAttribute('download', 'flow.png');
    a.setAttribute('href', dataUrl);
    a.click();
  }
</script>

Performance Tips

性能优化建议

  • Use
    $state.raw
    for nodes and edges (prevents deep reactivity)
  • For large graphs (1000+ nodes), set
    elevateNodesOnSelect={false}
    and
    elevateEdgesOnSelect={false}
  • Use
    minZoom
    and
    maxZoom
    to limit zoom levels
  • Consider virtualization for extremely large graphs
  • 对节点和边使用
    $state.raw
    (避免深度响应式监听)
  • 对于大型图(1000+节点),设置
    elevateNodesOnSelect={false}
    elevateEdgesOnSelect={false}
  • 使用
    minZoom
    maxZoom
    限制缩放级别
  • 超大型图考虑使用虚拟化技术

Common Props

常用属性

svelte
<SvelteFlow
  bind:nodes
  bind:edges
  nodeTypes={customNodeTypes}
  edgeTypes={customEdgeTypes}
  defaultEdgeOptions={{ animated: true, type: 'smoothstep' }}
  connectionLineType={ConnectionLineType.Straight}
  connectionMode={ConnectionMode.Loose}
  fitView
  minZoom={0.1}
  maxZoom={2}
  snapToGrid={true}
  snapGrid={[15, 15]}
  elevateNodesOnSelect={true}
  elevateEdgesOnSelect={true}
  deleteKeyCode="Delete"
  selectionKeyCode="Shift"
  multiSelectionKeyCode="Meta"
  panOnScroll={true}
  zoomOnScroll={true}
  zoomOnDoubleClick={true}
  zoomOnPinch={true}
  panOnDrag={true}
/>
svelte
<SvelteFlow
  bind:nodes
  bind:edges
  nodeTypes={customNodeTypes}
  edgeTypes={customEdgeTypes}
  defaultEdgeOptions={{ animated: true, type: 'smoothstep' }}
  connectionLineType={ConnectionLineType.Straight}
  connectionMode={ConnectionMode.Loose}
  fitView
  minZoom={0.1}
  maxZoom={2}
  snapToGrid={true}
  snapGrid={[15, 15]}
  elevateNodesOnSelect={true}
  elevateEdgesOnSelect={true}
  deleteKeyCode="Delete"
  selectionKeyCode="Shift"
  multiSelectionKeyCode="Meta"
  panOnScroll={true}
  zoomOnScroll={true}
  zoomOnDoubleClick={true}
  zoomOnPinch={true}
  panOnDrag={true}
/>