Svelvet 7 — Build Intuitive, Interactive Node-Based UIs in Svelte

Jen Lee
9 min readApr 20, 2023

Introduction

Today, we’re excited to announce the release of Svelvet 7, the most substantial update in Svelvet’s history. It features a re-imagining of many of the core features and adds dozens of new ones developers have been asking for. Svelvet 7 offers limitless customization, improved accessibility, intuitive modularity and powerful new functionality inspired by battle-tested UIs in software like Blender and Houdini. We’re beyond excited to get this into the hands of developers and can’t wait to see the incredible new applications it enables.

a user interface consisting of nodes and edges

Svelvet’s goal is to allow the creation of fully customizable, interactive node-based user-interfaces using the Svelte framework. Since its release in early 2022, Svelvet has developed a vigorous userbase, and version 7 brings a host of new features. These include new default themes, an improved Minimap, functional connections between Nodes, stateful Anchors, Group and selection boxes, a component-first API and the use of markdown-style text input to quickly render simple flow charts. Accessibility and user experience have been improved throughout, with code improvements, bug fixes, and a smaller package size.

Components

Svelvet 7 exposes seven new components that developers can use to compose graphs: Node, Anchor, Edge, Controls, Minimap, Group and Resizer.

Node

Developers can now interface with the Node component directly. It is configurable via props and features two-way data binding for many properties. No props are required, so getting started is as easy as importing Node and passing a few components to Svelvet. For a full list of props, check out our brand new documentation!

When using default Nodes, an arbitrary number of inputs and outputs can be specified. For the greatest level of flexibility and control, however, we suggest creating custom Nodes, which we discuss below.

<script>
import { Svelvet, Node} from 'svelvet';

let nodes = [
{
bgColor: 'red',
label: 'Node 1',
position: { x: 100, y: 100 },
selectionColor: rgb(255, 128, 200)
},
{
bgColor: 'green',
label: 'Node 2',
position: { x: 200, y: 200 }
},
{
id: 1,
bgColor: 'hsl(50, 20%, 50%)',
label: 'Node 3',
position: { x: 300, y: 300 }
}
];
<script>

<Svelvet theme="dark" edgeStyle="step" width={600} TD controls minimap>
{#each nodes as node}
<Node {...node} inputs={2} outputs={3}/>
{/each}
<Node rotation={-5} locked/>
</Svelvet>

For complete control over how Nodes are rendered, wrap the Node component around HTML elements. We expose a series of properties, functions and events that can be used to control the styling and logic of components. For instance, grabHandle is an action implemented via the use directive that can be placed on an element to handle touch and mouse events related to movement and selection. This can be the wrapping element, a header component or even an element positioned off the Node entirely.

<script lang="ts">
import { Node, Anchor } from 'svelvet';
import CustomEdge from './CustomEdge.svelte';

const connections = [4, "node1", ["node2", 5]]
</script>

<Node let:grabHandle let:selected let:node {id}>
<div class="node" use:grabHandle class:selected>
<button on:click={() => console.log("Hello!")}>Click Me</button>
<div class="input-anchors">
<Anchor input />
<Anchor input />
</div>
<div class="output-anchors">
<Anchor id="my-output" output {connections} />
<Anchor output {connections} />
<Anchor output edge={CustomEdge}/>
</div>
</div>
</Node>

Anchor

When building out custom Nodes, developers can now include an arbitrary number of Anchors positioned and styled with CSS. Anchors are configurable via props and can render out custom HTML elements that have been passed as children. Under the hood, Svelvet handles connection logic and exposes booleans related to their current state. Svelvet uses derived stores to maintain a record of Anchor positions and efficiently updates them only when the relative offset of the Anchor changes during resize or rotation events.

Anchors can be configured as input, output or no preference Anchors by passing (or not passing) the corresponding props. When passed, inputs can only be connected to outputs and vice versa. By default, Anchors can have an unlimited number of connections from Anchors of any kind.

Edges can be specificed ahead of time by passing an array of connections. This is a flexible prop that takes an array consisting of individual Node IDs in string or number form or a tuple of a Node ID and Anchor ID together. When specifying only Node IDs, we balance the connections between available Anchors.

<script>
import { Node, Anchor } from 'svelvet'
</script>

<Node id="55" let:grabHandle let:selected>
<div class="my-node-class">
<div class="input-anchors>
<Anchor let:linked let:hovering: let:connecting input>
<span class:connecting class="my-anchor"/>
</Anchor>
</div>
<div class="output-anchors">
<Anchor let:linked connections={[['nodeId', 'anchorId']]} output multiple>
<span class="my-anchor" class:linked />
</Anchor>
</div>
</div>
</Node>

<style>
.my-anchor {
width: 10px;
height: 10px;
border-radius: 2px;
background-color: blue;
}

.linked {
background-color: yellow;
}

.connecting {
background-color: white;
}
</style>

Edge

Developers can now create their own custom Edge components that can be passed as props to the Svelvet, Node or Anchor components.

To construct an Edge, create a new Svelte component and import Edge from Svelvet. Edges can be styled using props like color, width, animate, step and label or developers can use the exposed path to style their own HTML elements. When passing children, the Edge component accepts a “label” slot that will render out an element at the path’s midpoint.

<script >
import { Edge } from 'svelvet';
</script>

<Edge let:path let:destroy step>
<path d={path} />
<div on:click={destroy} class="my-label" slot="label">
<p>Custom Label</p>
</div>
</Edge>

<style>
path {
stroke: rgb(76, 20, 246);
stroke-width: 6px;
}
.my-label {
background-color: rgb(172, 2, 2);
width: 150px;
height: 100px;
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

To use these custom Edges, simply pass them via the edge prop available on the Svelvet, Node and Anchor components. With this approach, developers can set a default edge style at the graph level and then override it at the Node or Anchor level.

In addition to this new functionality, Svelvet 7 improves the reliability and performance of Edge interactions. Developers can now specify the width and targetColor of the ghost edge displayed on hover and can pass an edgeClick callback to be triggered on pointer events.

Controls

The Controls component contains a panel with zoom-in, zoom-out, fit view and lock/unlock buttons. Like ournew Background, Minimap and Theme Toggle components. Controls can be enabled an by passing the controls prop to the Svelvet component or further customized by importing the component and passing it as a child to the Svelvet wrapper.

Additionally, the Controls component can be infinitely customized by wrapping the it around custom button elements.

<script>
import { Controls } from 'svelvet';
</script>

<Controls let:zoomIn let:zoomOut let:fitView let:unhideAll corner="SW">
<button class="my-zoom-in" on:click|stopPropagation={zoomIn}/>
<button on:click|stopPropagation={zoomOut}/>
<button on:click|stopPropagation={fitView}/>
<button on:click|stopPropagation={unhideAll}/>
</Controls>

<style>
.my-zoom-in {
border: none;
background-color: black;
}
</style>

Minimap

We’re also introducing an all new Minimap component that features improved visualization and the ability to hide/unhide Nodes.

To render the Minimap using defaults, simply pass minimap as a prop to the Svelvet component. To customize it, import the Minimap component and pass it as a child of the Svelvet component, ensuring to label it with slot=”minimap” on the component itself. Default styling can be overriden with styling props such as mapColor, nodeColor and more. Developers can also now specify custom Minimap sizing using the width and height props.

<script>
import { Minimap, Svelvet } from 'svelvet'
</script>

<Svelvet locked controls>
<Node />
<Node />
<Node />
<Minimap corner="NE" hideable mapColor="blue" nodeColor="white" slot="minimap" />
</Svelvet>

Additional Features / Updates

Types

Our robust typing system is now available for use in TypeScript projects. We have types for Node configuration objects, valid CSS Color Strings and much, much more. Import them as types from ‘svelvet’.

Selection Boxes & Groups

Easily select multiple nodes with our new selection box. Enabled by default, users can hold down shift while clicking and dragging to select multiple Nodes to be moved as a unit. Similar functionality can be achieved without the use of a selection box by simply holding down the Shift key when selecting Nodes. To disable the selection box, pass the prop disableSelection to the Svelvet component.

Nodes can be grouped dynamically by holding Shift + Cmd while clicking and dragging or by wrapping a series of Nodes in a Group component.

Background Component

Backgrounds are now fully customizable via the Background component, with props for gridWidth, dotSize, bgColor and dotColor

Flowchart Diagram Layouts

Taking inspiration from MermaidJS, we have added the ability to construct a layered graph drawing using an intuitive text value. Developers can render these diagrams using any of our default Node components. This feature is currently in beta and we plan on adding additional functionality soon.

Data Flow

When creating Node graphs that go beyond simple diagrams, our native data flow tool enables functional connections, passing data between connected Anchors and Nodes.

As inputs and outputs are connected, the Svelte stores associated with those Anchors are automatically connected. We’re very excited about developing this feature with time and can’t wait to see what it enables. See the docs for more details.

Keyboard Controls

Users can now navigate the canvas using standard keyboard controls. When the graph is focused, use the arrow keys on to pan at normal speed. To pan more quickly, hold the shift key.

The “0”, “-”, and “=” keys reset, zoom out and zoom in the graph respectively.

Dark/Light Mode & Theme Toggle

Default styles can be set quickly by passing a theme prop to the Svelvet component. It defaults to light. Deveelopers can also enable a “Theme Toggle” component via props or by passing the ThemeToggle as a child of Svelvet.

Resize & Rotate Nodes

Nodes can now be dynamically resized and rotate by including the Resizer component in custom Nodes or by passing resizable to a default Node.

When hovering over the left or right edge of a Node, the cursor will update to show which dimensions allow for resizing. When hovering over the top left corner, a crosshair will appear, enabling rotation. Initial rotation can be specified by passing a numeric value in degrees as a Node prop.

Slider, Color Wheel & Radio Group

We’ve added three custom components with multiple methods of interaction that are designed to be used with our data flow system. Slider features increment/decrement buttons, slider functionality on drag, a text input field and keyboard controls via the up and down arrow keys. Basic styling is configurable via props. Color Wheel is a standard color wheel with a cursor representing the active selection. Radio Group allows users to specify a selection from a series of options.

Directionality

Graphs and Nodes can be specified as having top-down or left-to-right directionality. Passing TD or LR as a prop controls where input/output anchors are placed by default as well as the curvature of the Edges.

Dynamic Edge Attachment

Edges can now be attached/reattached dynamically.

Stacking Logic

Nodes now feature incrementing z-indexes when selected, placing the most recently selected Node on top. This behavior can be overriden by passing Infinity or -Infinity as the zIndex prop.

Improved Touch Controls

We’ve improved the reliability and consistency of touch interactions on mobile devices. We hope to soon have parity between the interactions that possible on mobile devices and those that are possible on desktop soon.

Website

We’ve revamped the website for Svelvet! We fixed several UI issues, reworked the hierarchy for the landing page, and added more media query breakpoints to improve page responsiveness.

Installation

Svelvet is available as an npm package.

To install, run the command below using npm:

npm install svelvet

Or yarn:

yarn add svelvet

Then checkout our new docs as well as the full changelog to get started building!

Svelvet 7.0 Team

Brian HolmesLinkedIn | GitHub

Britta AgerLinkedIn | GitHub

Thomas KadyLinkedIn | GitHub

Jen LeeLinkedIn | GitHub

--

--