Linked List Viz

A horizontal singly-linked-list visualization. Renders value-bearing circular nodes connected by directed arrows, with labeled pointers above (slow, fast, prev, curr) and a trailing terminator. Built on the shared SvgDefs arrow markers and the cb-* token system — there are no inline hex colors.

37149slow
Customize
Shape
5
1
Pointer
slow
accent

Installation

npx shadcn@latest add https://craftbits.dev/r/linked-list-viz.json

Usage

import { LinkedListViz } from "@craft-bits/core";
 
<LinkedListViz
  nodes={[
    { id: "n1", value: 3 },
    { id: "n2", value: 7 },
    { id: "n3", value: 1 },
    { id: "n4", value: 4 },
  ]}
  pointers={[
    { nodeId: "n2", label: "slow", tone: "accent" },
    { nodeId: "n4", label: "fast", tone: "warning" },
  ]}
  nodeStyles={{ n2: "highlight", n4: "highlight" }}
  showHead
/>

Reverse-list scaffolding — three pointers, mid-iteration:

<LinkedListViz
  nodes={[{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]}
  pointers={[
    { nodeId: 0, label: "prev", tone: "error" },
    { nodeId: 1, label: "curr", tone: "accent" },
    { nodeId: 2, label: "next", tone: "success" },
  ]}
  nodeStyles={{ "0": "visited", "1": "highlight" }}
/>

Understanding the component

  1. SVG-based, single layer. The whole viz is a single <svg> — nodes are <circle> + <text>, edges are <motion.line> with the shared arrow marker. No HTML overlay, no foreignObject. The viewBox auto-sizes from nodes.length, nodeSize, and nodeSpacing.
  2. Shared SvgDefs. Arrow markers come from <SvgDefs> (the canonical defs block), not local <marker> declarations. The hex="currentColor" binding lets the parent's text-cb-* class drive the marker color.
  3. Four node styles. nodeStyles[id] selects from "default" (neutral elevated surface), "highlight" (accent ring + accent-muted fill — the "current" node), "visited" (muted, no glow), and "dimmed" (low-opacity ghost for unreachable / lookahead).
  4. Pointer labels are SVG groups. Each pointer is a <motion.g> with the label text + a small tick mark. The group's x animates with SPRINGS.smooth so the label glides between nodes — no AnimatePresence remounts on move.
  5. Null terminator. When nullTerminated (default true), a small arrow + glyph render after the last node. This is the canonical "next === null" marker; it makes the list visually closed.
  6. Reduced motion. usePrefersReducedMotion() short-circuits every transition to { duration: 0 } — pointer moves, node entries, and arrow draws all snap into place.

Props

PropTypeDefaultDescription
nodesreadonly { value: string | number; id?: string | number }[]requiredOrdered list of nodes (left → right).
pointersreadonly { nodeId; label; tone? }[]Labels above target nodes (slow, fast, prev, …).
nodeStylesRecord<string, "default" | "highlight" | "visited" | "dimmed">Per-node visual override keyed by node id (or index string).
showHeadbooleanfalseRender a head marker arrow before the first node.
showTailbooleanfalseRender a tail marker arrow below the last node.
nullTerminatedbooleantrueAppend the terminus after the last node.
nodeSizenumber40Diameter in pixels.
nodeSpacingnumber80Horizontal spacing between node centers.
classNamestringMerged onto the outer <svg>.

Accessibility

  • The outer <svg> is role="img" with an aria-label that flattens the list to a screen-readable string ("Linked list: 3 -> 7 -> 1 -> 4 -> null").
  • Decorative glyphs (the terminus, pointer ticks, head / tail labels) are visual chrome — the readable narrative lives in aria-label.
  • Color is never the only signal — every node renders its numeric or string value, and pointer labels carry their own text ("slow", "prev", …), so the viz stays legible for colorblind users and at every theme setting.
  • Motion respects prefers-reduced-motion: pointer moves and node entries collapse to instant when the user has opted out.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/viz/LinkedListViz.tsx). The original was a rich reversal-lesson widget bundled with shatter particles, sever-point geometry, ghost trails, and an unreachable-bracket overlay — the library extract is the linked-list primitive itself: nodes, arrows, pointers, head / tail markers, and a null terminus.