npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@formvue/react-workflow-kit

v0.2.0

Published

Headless React workflow/flow-builder kit: graph operations, React Flow canvas machinery, screen shells, form-field renderers and Puck block layer. Bring your own shadcn primitives, theme tokens, persistence and node-kind policy.

Readme

@formvue/react-workflow-kit

Headless React kit for building flow/workflow editors: a React Flow graph canvas, screen shells and renderers, a Puck block layer, and pure graph operations — extracted from FormVue's form builder and generified so any app (a form builder, a checkout-flow editor, …) can drive it with its own domain.

Headless means the kit brings behavior; the host brings:

  • shadcn primitives — via the @formvue/react-workflow-kit/ui bundler alias
  • theme — all kit classes are semantic shadcn tokens (bg-background, …)
  • persistence — a FlowRepository implementation (the kit never calls an API)
  • domain rules — a NodeKindPolicy describing its screen kinds
  • node visuals — a NodeRegistry mapping kinds to React Flow components

Subpaths

| Import | Contents | Extra peers | |---|---|---| | @formvue/react-workflow-kit | ports (NodeKindPolicy, FlowRepository, NodeRegistry types), WorkflowKitProvider, error boundaries, createInMemoryFlowRepository, node geometry constants | — | | …/graph | pure graph ops: validateGraph, insertScreenBetween, removeScreenFromGraph, computeGraphVariants, computeTraversalOrder, dagre layout, condition evaluator | — (no React) | | …/schemas | question/decision screen zod schemas, field-type manifest, dynamic getFieldSchema/buildFormSchema answer validation | zod, libphonenumber-js | | …/shell | ScreenShell/ContentShell/BaseShell, navigation footer + progress, navigation/config/preview/runtime contexts, PhoneFrame, SyncStatusIndicator | — | | …/flow | useFlowGraph, FlowCanvas, node/edge chrome, canConnect, editor-mode machinery, keyboard shortcuts | @xyflow/react, @tanstack/react-store | | …/renderers | question screen contents + form-field inputs on react-hook-form | react-hook-form, zod | | …/blocks | Puck block catalog (createBuilderConfig), PuckEditor, PuckContent, schema versioning, block manifests | @puckeditor/core |

Peers are optional per subpath — a consumer using only /graph + /flow installs neither Puck nor react-hook-form.

The ui adapter (required for /shell, /flow, /renderers, /blocks)

The kit imports primitives from @formvue/react-workflow-kit/ui, which stays external in the build. Point it at your shadcn components:

// src/lib/workflow-kit-ui.ts — re-export YOUR shadcn primitives
export { Button } from "@revenue/ui/components/button";
export { Input } from "@revenue/ui/components/input";
// … every member of the contract (see ui-contract.d.ts) …

// Conformance check — fails the build if a primitive drifts:
import type * as Contract from "@formvue/react-workflow-kit/ui";
// vite.config.ts (AND vitest.config.ts if separate)
resolve: {
  alias: {
    "@formvue/react-workflow-kit/ui": path.resolve(__dirname, "src/lib/workflow-kit-ui.ts"),
  },
},

TypeScript resolves …/ui to the published ui-contract.d.ts automatically — no tsconfig changes needed. An un-aliased build fails loudly at bundle time.

Both known hosts run shadcn on Base UI (render prop, no Radix asChild); the contract is written from that intersection.

CSS

/* your global stylesheet (Tailwind v4) */
@import "@formvue/react-workflow-kit/styles.css";      /* @source scan of kit files */
@import "@formvue/react-workflow-kit/flow-canvas.css";  /* React Flow fixes (with @xyflow/react/dist/style.css) */
@import "@formvue/react-workflow-kit/puck-theme.css";   /* Puck editor chrome (optional) */

styles.css carries the @source directive relative to its own dist location, so the consumer's Tailwind generates the kit's classes even under pnpm symlinks. All classes are semantic shadcn tokens — your theme's --background/--primary/… mapping styles the kit for free, dark mode included.

WorkflowKitProvider (optional host services)

<WorkflowKitProvider
  optimizeImageUrl={(url, { width }) => cdn(url, width)}  // identity by default
  onAnalyticsEvent={(e, p) => posthog.capture(e, p)}       // noop by default
  toast={{ success: toast.success, error: toast.error }}   // noop by default
  AddressInput={MyAddressAutocomplete}   // /renderers address field (else plain input)
  ConsentSection={MyLegalCopy}           // ScreenShell entry-screen slot (else nothing)
  renderMediaIcon={(name, cls) => <DynamicIcon name={name} className={cls} />}
  AnimationPlayer={Lottie}
  IconPicker={MyIconPicker}              // /blocks icon field (else text input)
>

Minimal canvas

const policy = defineNodeKindPolicy<CheckoutKind, CheckoutScreen>({
  entryKinds: ["start"],
  terminalKinds: ["confirmation"],
  requiredKinds: ["start", "payment", "confirmation"],
  singletonKinds: ["payment"],
  trailingOrder: ["payment", "confirmation"],   // derives the protected edge
  restrictedTargets: { payment: ["confirmation"] },
});

const registry: NodeRegistry<CheckoutScreen> = {
  nodeTypes: { checkout: CheckoutNode },
  nodeTypeFor: () => "checkout",
  labelFor: (s) => s.name ?? s.type,
  heightFor: () => 80,
};

function Editor() {
  const repository = useInMemoryFlowRepository(repo); // or your own FlowRepository
  const flow = useFlowGraph({ repository, policy, registry });
  return (
    <ReactFlowProvider>
      <FlowCanvas
        nodes={flow.nodes} edges={flow.edges} nodeTypes={registry.nodeTypes}
        onNodesChange={flow.onNodesChange} onEdgesChange={flow.onEdgesChange}
        onConnect={flow.onConnect} onNodeClick={flow.onNodeClick}
        onNodeDragStop={flow.onNodeDragStop}
        isValidConnection={flow.isValidConnection}
        fitViewOnReady={flow.initialLayoutDone}
      />
    </ReactFlowProvider>
  );
}

See playground/ for the complete checkout-flow example.

FlowRepository

The persistence port. The kit reads screens/edges/entryScreenId/ syncStatus and calls the mutators; how they persist is yours — optimistic cache updates, Convex mutations, TanStack DB (formvu-web's flow-db-context is the canonical rich implementation of this interface).

  • createInMemoryFlowRepository() is the reference implementation (playground, tests, PoCs) with subscribe/getSnapshot for useSyncExternalStore (useInMemoryFlowRepository wraps that).
  • The contract test suite lives in this repo at src/test-utils/repository-contract.ts (not shipped in the npm package) — copy it next to your implementation and run it to prove conformance.

SSR (TanStack Start / Cloudflare Workers)

The canvas must never execute during SSR. Double indirection is mandatory — anything statically imported by a route file lands in the SSR bundle:

// route file
const Canvas = React.lazy(() => import("#/components/checkout-flow/canvas"));
// render
<ClientOnly fallback={<Skeleton />}>
  <Suspense><Canvas /></Suspense>
</ClientOnly>

@xyflow/react and dagre then only load in the browser.

Development

pnpm dev      # playground (vite) — aliases /ui to playground/src/ui
pnpm check    # biome
pnpm typecheck
pnpm test     # vitest
pnpm build    # tsc gate + vite lib build + per-entry d.ts + css copy

Releases via changesets: PRs snapshot-publish as @formvue/react-workflow-kit@pr-<N>; merging the "chore: version packages" PR publishes to npm.