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

@vergelijkdirect/comparison-forms

v2.0.2

Published

Headless Vue 3 comparison forms for vergelijkdirect — consumable as a Vue 3 library or as self-contained custom elements.

Readme

@vergelijkdirect/comparison-forms — Vue 3 library (2.x)

Headless comparison forms for vergelijkdirect, consumable two ways:

  • Vue 3 hosts — npm package export @vergelijkdirect/comparison-forms/vue
  • Vue 2.7 / non-Vue hosts — self-contained Web Component @vergelijkdirect/comparison-forms/wc

The library is the whole of src/. The legacy Vue 2.7 app it replaced was migrated form by form and has since been removed — its <embedded-comparison-forms> plugin API does not exist here (see the migration table below). If you still need 1.x, it lives on the v1 branch, in maintenance mode: bugfixes and security only, no new forms or features.

Install & prerequisites

The ITC client (@vergelijkdirect/insurance-transmission-client) is your peer dependency: the library never creates or exports it for Vue hosts. You install and initialize it yourself, once, before mounting any form — the library's forms/flow lazily pull the already-initialized services (see preview/main.ts for a working bootstrap example).

npm i @vergelijkdirect/comparison-forms

Load the styles once per page:

import '@vergelijkdirect/comparison-forms/vue/styles';

Usage

1. Drop-in (recommended for VD) — the form handles everything

<script setup>
import { ComparisonFormFlow } from '@vergelijkdirect/comparison-forms/vue';
// ITC must already be initialized (see "Install & prerequisites" above)
</script>

<template>
    <ComparisonFormFlow
        form="bike"
        link="https://www.vergelijkdirect.com/fietsverzekering/"
        layout="horizontal"
        @submitted="(url) => track(url)"
        @error="showFallbackLead"
    />
</template>

Fills the form → creates the comparison via ITC → appends the comparison route (#/<id>/…) to the base link → redirects. On failure it emits error(error, payload) and does nothing else — the fallback-lead UI is yours to wire, with the user's data available in payload.user.

2. Headless — you own the submit

<script setup>
import {
    BikeForm,
    bikePresenter,
    useComparisonSubmit,
} from '@vergelijkdirect/comparison-forms/vue';

const { submit, isSubmitting } = useComparisonSubmit({
    link: 'https://…/fietsverzekering/funnel/',
    onError: showFallbackLead,
});
</script>

<template>
    <BikeForm @submitted="(data) => submit(bikePresenter(data))" />
</template>

Or skip the flow entirely (result-page edit, custom persistence):

<BikeForm :initial-data="savedBike" @submitted="updateMyComparison" />

initialData is read once on mount — remount with :key when it arrives async.

3. Web Component (Vue 2.7 / plain HTML hosts)

<script
    type="module"
    src=".../comparison-forms/wc"
></script>
<link
    rel="stylesheet"
    href=".../comparison-forms/wc/styles"
/>

<vd-comparison-form
    form="bike"
    link="https://…/fietsverzekering/funnel/"
    api-url="https://api.vergelijkdirect.com"
></vd-comparison-form>

<script>
    document
        .querySelector('vd-comparison-form')
        .addEventListener('error', (e) => console.error(e.detail[0]));
</script>

The element initializes the shared ITC client from the url attributes — it is its own consumer, no host-side setup needed. Events are native CustomEvents (submitted, error) with the emit arguments in detailerror carries the error in detail[0] and, when the failure happened on submit, the ComparisonPayload in detail[1] (client-init failures have no payload). initial-data accepts either a JSON-string attribute (initial-data='{"kind":1}', for plain HTML) or an object set as a DOM property (el.initialData = {…}); other complex props are DOM properties only.

Migrating from the 1.x <embedded-comparison-forms> events

The 2.x event contract is a deliberate break. How each legacy event maps:

| Legacy (1.x) | When it fired | 2.x equivalent | | ------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @change | result/coverage page — "update, don't create" | headless @submitted with typed form data; run it through the form's presenter and payload.user carries the fields hosts wrote back (zipCode, birthdate, houseNumber, houseNumberAddition, street, city) | | @emit-event { event: 'open-quotation-modal', data } | comparison create failed → fallback lead | @error — emits (error, payload); build the fallback lead from payload.user. ITC rejects an unknown postcode the same way as a transport failure — check error instanceof Error if you need to tell them apart | | @emit-event (no arguments) | submitted with unchanged data → "close me" | none by design — the form always emits submitted; the host owns update semantics, so compare the submitted data with what it passed as initial-data (e.g. lodash isEqual) and skip the update | | @redirect-page | never reached hosts — the 1.x embedded shell consumed it internally | submitted(url) fires before the redirect; the redirect prop overrides navigation |

Development

npm run dev            # live sandbox (preview/) — demos for every component and form
npm run verify         # lint + format:check + typecheck + test:unit — the one gate
npm run test:unit      # vitest run
npm run typecheck      # vue-tsc --noEmit
npm run lint           # eslint src tests preview  (lint:fix to autofix)
npm run format         # prettier --write .        (format:check to verify only)

Node version is pinned in .nvmrc (Node 24) — run nvm use (or fnm) before installing. Install with npm ci, not npm install: it installs exactly from the committed package-lock.json and avoids the npm optional-dependency bug (npm#4828) that can drop rolldown's native binding and break vite. If a machine gets into that state, a clean rm -rf node_modules && npm ci fixes it.

Bumping a @vergelijkdirect/* package has rules of its own (exact pins, and a second step for ITC) — see DEPENDENCIES.md.

src/environment/environment.ts is gitignored — create it from the checked-in environment.dev.ts / environment.prod.ts first.

Builds

npm run build          # typecheck, then every build below (prepublishOnly runs verify + this)
npm run build:lib      # dist/vue      — Vue 3 library, vue external
npm run build:wc       # dist/wc       — self-contained <vd-comparison-form> (ES; ITC + Vue inlined)
npm run build:wc-noitc # dist/wc-noitc — same element, UMD, ITC left external (host resolves it)
npm run build:wc-forms # dist/wc-forms — one CJS element bundle per form (<name>.cjs), ITC external

build:wc is the self-contained drop-in used from a plain <script> tag. The wc-noitc / wc-forms variants keep @vergelijkdirect/insurance-transmission-client external so a host bundler (webpack 5) shares its own configured client with the forms.

Publishing to npm

Manual — no pipeline publishes. Needs membership of the @vergelijkdirect npm org (npm whoami) and a clean working tree.

nvm use
npm ci
npm run verify                         # also runs on publish — but fail before the version commit

npm version <patch|minor|major|prerelease>   # bumps + commits; the tag it makes stays local
npm publish --tag <latest|next|legacy|rc>
git push

npm view @vergelijkdirect/comparison-forms dist-tags

--tag is not optional: a bare npm publish writes latest even for a prerelease. latest = stable 2.x, next = 2.x RCs, legacy = 1.3.x, rc = 1.x prereleases.

Architecture

entries (lib.ts, wc*.ts)  →  flow (useComparisonSubmit, ComparisonFormFlow)
  →  comparisons (registry + one dir per comparison: Form, schema, models, presenter)
    →  components (fields: address/licensePlate/company  →  ui: Vd* primitives)

entries  →  questionnaires  →  components     (one-off track; never through flow/ or comparisons/)
data/itc.ts — the single gateway to the ITC client
constants / models / utils / schemas / enums / interfaces — shared leaves, any layer may import

Path alias: @src/. The rules that matter:

  • Forms are headless. They validate (yup via useValidator) and emit submitted with typed, valid data — no API calls, no redirects, no global state. Whoever handles submitted decides the scenario.
  • One component per comparison. Vertical/horizontal is the layout prop, not a second file.
  • Dependency arrows point down only. Comparisons never import each other, and never flow/.
  • Only data/ touches the ITC client — everyone else goes through the @/data barrel.
  • Barrels export named symbols only. export * is an ESLint error under src/ (tree shaking).
  • All CSS comes from @vergelijkdirect/styles (.vd-*) — the library writes none of its own.

Adding a new comparison form

CONTRIBUTING.md — step by step, from the field list to the definition of done, including the seven places a new form must be registered.