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 🙏

© 2025 – Pkg Stats / Ryan Hefner

vanjs-form

v1.1.0

Published

Fully typed form state management library (with validation) for VanJS

Readme

VanJS Form

Fully typed form state management library (with validation) for the VanJS framework. If you are coming from React, vanjs-form feels similar to react-hook-form.

Install

yarn add vanjs-form vanjs-core yup
npm i -S vanjs-form vanjs-core yup

Features

  1. Dead simple Form API to create and manage the form state
  2. Fully typed method parameter and return types
  3. Supports validation with Yup. Zod and Joi coming soon!
  4. Built with developer experience (DX) in mind

QuickStart

import van from "vanjs-core";
import { Form, yupValidator } from "vanjs-form";
import * as yup from "yup";

const { div, h1, p, input, label, select, form: formEl, button, br } = van.tags;

function App() {
  const form = new Form({
    initialValues: {
      title: "",
      name: "",
      age: 0,
      gender: ""
    },
    validator: yupValidator(
      yup.object({
        title: yup.string().required("Required"),
        name: yup.string().required("Required"),
        age: yup.number().required("Required").min(18, "At least 18 years"),
        gender: yup.string().oneOf(["Male", "Female"], "Valid gender required")
      })
    ),
    validationMode: "oninput" // Validation is run on input as well as on submit. Defaults to 'onsubmit'
  });

  // Watch values when they change in the form fields
  const observables = form.watch("name", "title");
  // or
  const allValues = form.watch();

  // Called when form passes validation
  const onsubmit = form.handleSubmit((values) => {
    console.log({ submittedValues: values });
  });

  return div(
    h1(() => `VanJS Form for ${observables.val.title} ${observables.val.name}`),

    formEl(
      { onsubmit: onsubmit },
      label("Title"),
      div(
        label(
          input(form.register("title", { type: "radio", value: "Mr." })),
          "Mr."
        ),
        label(
          input(form.register("title", { type: "radio", value: "Ms." })),
          "Ms."
        ),
        label(
          input(form.register("title", { type: "radio", value: "Mrs." })),
          "Mrs."
        )
      ),

      br(),

      label("Name"),
      input(form.register("name", { autofocus: true })),

      br(),

      label("Age"),
      input(form.register("age", { type: "number", min: "0", max: "100" })),

      br(),

      label("Gender"),
      select(
        form.register("gender", { type: "number", min: "0", max: "100" }),
        option({ value: "" }, "Select..."),
        option({ value: "Male" }, "Male"),
        option({ value: "Female" }, "Female")
      ),

      br(),

      button({ type: "submit" }, "Submit")
    )
  );
}

van.add(document.body, App());

API Reference

Form

  • The Form class controls the form state and has methods to manipulate this state
  • If validator parameter is provided, the submit handler is called if validation passes
  • Use validationMode to control whether validation should occur on input or only on submit
import { Form } from "vanjs-form";

const form = new Form({
  initialValues: Record<string, any>,
  validator: (values: Record<string, any>) => Record<string, any> | FormError,
  validationMode: "onsubmit" | "oninput" // Defaults to onsubmit
});

After the instance is created, you can access the form functions to work with your form.

YupValidator

  • The yupValidator function takes a Yup ObjectSchema and returns a validator function

Zod and Joi validators coming soon!

const form = new Form({
  initialValues: {
    title: "",
    name: "",
    age: 0,
    gender: ""
  },
  validator: yupValidator(
    yup.object({
      title: yup.string().required("Required"),
      name: yup.string().required("Required"),
      age: yup.number().required("Required").min(18, "At least 18 years"),
      gender: yup.string().oneOf(["Male", "Female"], "Valid gender required")
    })
  )
});

Form methods

import { Form } from "vanjs-form";

const form = new Form({
  initialValues: Record<string, any>,
  validator: (values: Record<string, any>) => Record<string, any> | FormError,
  validationMode: "onsubmit" | "oninput" // Defaults to onsubmit
});

form.register();
form.get();
form.set();
form.error();
form.watch();
form.reset();
form.handleSubmit();

Register

  • Registers an input with the form
  • Merges additionalProps into returned object
  • Returns an object of props (name, value, checked, oninput, onfocus) which should be assigned as the element's props
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true,
    location: ""
  }
});

return div(
  input(form.register("name", { type: "text", placeholder: "Enter your name" })),
  input(form.register("age", { type: "number", min: 0, max: 100 })),
  input(form.register("foo", { type: "checkbox" })),
  select(
    form.register("select"),
    option({ value: "home" }, "Home"),
    option({ value: "work" }, "Work")
  )
);

Get

  • Gets the typed value of the form field with the name key
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  }
});

const name = form.get("name");
console.log(name); // "Kwame"

const age = form.get("age");
console.log(age); // 28

const foo = form.get("foo");
console.log(foo); // true

Set

  • Sets the value of the form field with the name key
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  }
});

form.set("name", "Opare");
form.set("age", 29);
form.set("foo", false);

Error

  • Gets the error string of the form field with the name key
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  },
  validator: yupValidator(
    yup.object({
      name: yup.string().required("Required").min(6, "Must be more than 6 characters"),
      age: yup.number().required("Required"),
      foo: yup.boolean().required("Required"),
    })
  ),
  validationMode: "oninput"
});

const nameError = form.error("name");
console.log(nameError); // "Must be more than 6 characters"

const ageError = form.error("age");
console.log(ageError); // ""

const fooError = form.error("foo");
console.log(fooError); // ""

An empty string is returned if the field has no error

Watch

  • Returns a reactive object containing the specified field values
  • No need to wrap in van.derive() 🙂
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  },
});

const full = form.watch();
const partial = form.watch("name", "age");

return div(
  p(() => `Full data: ${JSON.stringify(full)}`), // Renders: Full data { "name": "Kwame", "age": 28, "foo": true }
  p(() => `Partial data: ${JSON.stringify(partial)}`), // Renders: Partial data { "name": "Kwame", "age": 28 }
);

Errors

  • Returns a reactive object containing the specified field errors
  • No need to wrap in van.derive() 🙂
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  },
  validator: yupValidator(
    yup.object({
      name: yup.string().required("Required").min(6, "Must be more than 6 characters"),
      age: yup.number().required("Required"),
      foo: yup.boolean().required("Required"),
    })
  ),
  validationMode: "oninput"
});

const errors = form.errors();
const partial = form.errors("name", "age");

return div(
  p(() => `Full errors: ${JSON.stringify(errors)}`), // Renders: Full errors { "name": "Must be more than 6 characters", "age": "", "foo": "" }
  p(() => `Partial errors: ${JSON.stringify(partial)}`), // Renders: Partial errors { "name": "Must be more than 6 characters", "age": "" }
);

Reset

  • Resets a form field or the entire form
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  }
});

form.reset("name"); // Reset name field
form.reset("name", "age"); // Reset name and fields
form.reset(); // Reset all fields

Handle submit

  • Register a function to use as a submit handler
  • If validator is specified on the form, the handler is called if validation passes
  • Returns a new function to be passed to the form element
const form = new Form({
  initialValues: {
    name: "Kwame",
    age: 28,
    foo: true
  }
});

const handleSubmit = form.handleSubmit((values) => {
  console.log(values);
});

return form(
  { onsubmit: handleSubmit },
  // ... Form children
)

Contributors

Changelog

  • 1.1.0 (Current)
    • Added Form.errors() method
  • 1.0.2
    • Disabled call to form.validateField on form.reset to avoid errors on form reset