Connector Edge
Curved edge between canvas objects with optional inline label state. Install Connector Edge from the VLLNT UI registry with the shadcn CLI.
Curved edge between canvas objects with optional inline label state. Part of the utility family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.
Preview
Switch between light and dark to inspect the embedded Storybook preview.
Installation
Add Connector Edge to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/connector-edge.jsonSource
import type { CSSProperties } from "react";
import { cn } from "../../lib/utils";
import { EdgeLabel } from "../edge-label/edge-label";
export type ConnectorEdgePoint = {
x: number;
y: number;
};
export type ConnectorEdgeProps = React.ComponentPropsWithoutRef<"div"> & {
end: ConnectorEdgePoint;
label?: string;
start: ConnectorEdgePoint;
state?: "active" | "blocked" | "idle";
};
const strokeClasses: Record<
NonNullable<ConnectorEdgeProps["state"]>,
string
> = {
active: "stroke-sky-500",
blocked: "stroke-amber-500",
idle: "stroke-muted-foreground/60",
};
const ConnectorEdge = ({
className,
end,
label,
ref,
start,
state = "idle",
...props
}: ConnectorEdgeProps & { ref?: React.Ref<HTMLDivElement> }) => {
const width = Math.max(Math.abs(end.x - start.x), 32);
const height = Math.max(Math.abs(end.y - start.y), 32);
const midX = width / 2;
const startX = start.x <= end.x ? 4 : width - 4;
const endX = start.x <= end.x ? width - 4 : 4;
const startY = start.y <= end.y ? 4 : height - 4;
const endY = start.y <= end.y ? height - 4 : 4;
const path = `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`;
const style = {
height,
width,
} satisfies CSSProperties;
return (
<div
className={cn("relative inline-flex", className)}
ref={ref}
style={style}
{...props}
>
<svg
className="overflow-visible"
height={height}
viewBox={`0 0 ${width} ${height}`}
width={width}
>
<path
className={cn("fill-none stroke-[2.5]", strokeClasses[state])}
d={path}
strokeDasharray={state === "blocked" ? "4 4" : undefined}
strokeLinecap="round"
/>
</svg>
{label ? (
<EdgeLabel
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
emphasis={state === "active" ? "active" : "subtle"}
>
{label}
</EdgeLabel>
) : null}
</div>
);
};
ConnectorEdge.displayName = "ConnectorEdge";
export { ConnectorEdge };
Stories
Explore every variant and state in the interactive Storybook:
Preview
Switch between light and dark to inspect the embedded Storybook preview.