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

@rakit-ui/form-core

v1.1.0

Published

A tiny, framework-agnostic headless form engine with a simple API.

Downloads

14

Readme

@rakit-ui/form-core

A tiny, framework-agnostic headless form engine with a simple API.

  • Headless: logic only, no UI opinions.
  • Framework-agnostic: works in React, Vue, Angular, or vanilla JS.
  • Tiny: ~7 KB ESM, zero dependencies.
  • Simple API: values, errors, touched, dirty.

Installation

pnpm add @rakit-ui/form-core
# or
npm install @rakit-ui/form-core

Requires Node.js 20+ and a modern bundler. ESM-only.

Basic usage

import { createForm } from "@rakit-ui/form-core";

const form = createForm({
  initialValues: {
    email: "",
    password: "",
    profile: { name: "" },
  },
  validate(values) {
    const errors: Record<string, string> = {};
    if (!values.email) errors.email = "Email is required";
    if (!values.password) errors.password = "Password is required";
    if (!values.profile.name) errors["profile.name"] = "Name is required";
    return errors;
  },
  async onSubmit(values) {
    console.log("submit", values);
  },
});

form.setValue("email", "[email protected]");
form.setTouched("email", true);

const result = await form.submit();
// result: { ok: true, errors: {} } | { ok: false, errors: {...} }

Core API

  • createForm(options) — create a form instance.
  • setValue(path, value) / getValue(path) — read/write values via dotted paths (profile.name, addresses.0.city).
  • setValues(values) — replace all values (clears dirty).
  • setError(path, error?) / setErrors(errors) — manage errors.
  • setTouched(path, bool) / setDirty(path, bool) — mutate metadata.
  • getField(path) — ergonomic helper with onChange, onBlur, setValue, setTouched.
  • validate() — run validator, returns latest errors. Supports sync and async.
  • submit() — returns Promise<{ ok, errors }>.
  • reset(nextValues?) — reset to initial snapshot or a new one (wipes all interaction state).
  • subscribe(listener) — subscribe to full state updates.
  • destroy() — clear listeners and disable mutators.

New in v1.1

  • subscribeField(path, listener) — subscribe to one field. Listener fires only when that field's FieldState changes (exact path match, referential diff).
  • resetField(path) — reset one field (and its descendant subtree metadata) without touching siblings.
  • validateField(path) — run the validator but apply only errors[path]. Race-safe per path.
  • setInitialValues(values) — swap the baseline and clear dirty, but keep errors, touched, submitCount. Useful after server refresh.
  • isValid() / isDirty() — sync derived helpers.

See CHANGELOG.md for the full list.

Key design decisions (v1.0.0)

  • Flat error/touched/dirty records keyed by path string.
  • Array index paths: numeric segments like addresses.0.city create arrays, non-numeric create objects.
  • Concurrent submit() is a no-op — the second call returns { ok: false, errors } without touching state.
  • Parallel validate() calls: only the latest result writes to state.
  • destroy() disables state mutations so in-flight async tasks become no-ops.
  • Dirty comparison is referential (!==) — object/array contents are not deep-compared in v1. Deep equality is on the v1.2 roadmap.

See the PRD in the repo for full rationale and roadmap.

License

MIT