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

@juantroconisf/lib

v11.11.0

Published

A form validation library for HeroUI.

Readme

@juantroconisf/lib

npm version License: ISC

A type-safe form management library for React and HeroUI. It eliminates boilerplate through a declarative on.* API that bridges your Yup schema directly to HeroUI component props — complete with validation, error messages, dirty tracking, and ID-based array management.

Why this library?

Managing forms in React often involves manual state syncing and verbose validation logic. This library acts as a bridge, automatically mapping your Yup schema's constraints directly to your HeroUI components.

Key Benefits

  • Zero Boilerplate: No more manual value, onChange, and onBlur wiring.
  • Type-Safe: Automatic inference from your schema with full Intellisense.
  • Auto-Validation: Errors and isRequired states flow naturally from your schema.
  • ID-Based Arrays: Seamless handling of dynamic lists with $O(1)$ performance.

Table of Contents


Quick Start

Get up and running in under a minute.

1. Install

pnpm add @juantroconisf/lib yup

2. Implementation

import { useForm } from "@juantroconisf/lib";
import { string, boolean } from "yup";
import { Input, Switch, Button } from "@heroui/react";

const MyForm = () => {
  const { on, ControlledForm } = useForm({
    fullName: string().required().default(""),
    darkMode: boolean().default(false),
  });

  return (
    <ControlledForm onSubmit={(data) => console.log(data)}>
      {/* on.input() automatically handles value, onValueChange, isInvalid, and errorMessage */}
      <Input {...on.input("fullName")} label='Full Name' />
      
      <Switch {...on.switch("darkMode")}>Dark Mode</Switch>
      
      <Button type='submit' color='primary'>
        Submit
      </Button>
    </ControlledForm>
  );
};

How-to Guides

Practical recipes for common form scenarios.

1. Working with Nested Objects

Dot notation reaches arbitrarily deep. TypeScript validates that the path exists and has the correct type.

<Input {...on.input("settings.profile.username")} label="Username" />
<Switch {...on.switch("settings.notifications")}>
  Enable Notifications
</Switch>

2. Managing Dynamic Arrays

Arrays of objects are tracked by item id (or a custom identifier). This ensures inputs hold their state correctly even after re-ordering or deletions.

{state.users.map((user) => (
  <div key={user.id}>
    {/* Bind to a field inside the array item using: path + itemId */}
    <Input {...on.input("users.name", user.id)} label='Name' />
    
    <Button onPress={() => helpers.removeById("users", user.id)}>
      Remove
    </Button>
  </div>
))}

<Button onPress={() => helpers.addItem("users", { id: Date.now(), name: "" })}>
  Add User
</Button>

3. N-Level Deep Structures

For arrays inside objects inside arrays, pass a variadic sequence alternating between structural paths and identifiers.

// Schema: items[].form_response.input_values[].value
<Input
  {...on.input("items", itemId, "form_response.input_values", inputId, "value")}
  label='Deep Value'
/>

4. Performing Manual Updates

To update state outside of a component (e.g., in an API response handler), use the manual setters.

const { onFieldChange, onArrayItemChange } = useForm(schema);

// Update a top-level or nested field
onFieldChange("settings.theme", "dark");

// Update a field inside an array item
onArrayItemChange({ at: "users.name", id: userId, value: "Alice" });

Reference

useForm(schema, options?)

| Option | Type | Description | | :--- | :--- | :--- | | arrayIdentifiers | Record<string, string> | Override the ID field (default: "id") | | resetOnSubmit | boolean | Reset form after success (default: false) | | keepValues | (keyof State)[] | Fields to exclude from reset |

Validation Results

The validateAll, validateItem, and the helpers validation methods return a ValidationResponse object:

// Top level
const { isValid, results } = await validateAll();
const { isValid, results } = await validateFields(["firstName", "lastName"]);

// Within helpers
const { isValid, results } = await helpers.validateItem("users", userId);

isValid: boolean; errors: string[]; // List of composite IDs with errors results: ErrorResult[]; // Detailed error objects }

interface ErrorResult { id: string; // e.g. "users.0.name" label: string; // resolved from DOM (e.g. "User Name") or prettified ID message: string; // validation error message }


#### Automatic Label Capture
The library lazily resolves labels the first time a field is blurred or validated. It checks in priority:
1. `aria-labelledby` element's text.
2. Standard `<label for="...">` text.
3. Fallback: Prettified last segment of the ID (capitalized).

### The `on.*` API
Methods that bridge schema logic to HeroUI components.

| Method | HeroUI Component | Key Props Returned |
| :--- | :--- | :--- |
| `on.input()` | `Input`, `Textarea` | `value`, `onValueChange` |
| `on.select()` | `Select` | `selectedKeys`, `onSelectionChange` |
| `on.switch()` | `Switch` | `isSelected`, `onValueChange` |
| `on.checkbox()` | `Checkbox` | `isSelected`, `onValueChange` |
| `on.radio()` | `RadioGroup` | `value`, `onValueChange` |

### Array Helpers (`helpers.*`)
| Method | Description |
| :--- | :--- |
| `addItem(path, item)` | Append a new item |
| `removeById(path, id)` | Fast $O(1)$ removal by ID |
| `updateById(path, id, partial)` | Shallow merge by ID |
| `moveById(path, fromId, toId)` | Reorder items |

---

## Explanation

### The Bridge Architecture
Traditional form libraries often require you to manually map state to component props (`value={state.foo} onChange={...}`). 

This library uses a **Bridge Pattern**:
1. Your **Schema** defines the "Source of Truth".
2. The **`on.*` methods** act as the bridge, translating that truth into the specific prop contract needed by each HeroUI component.
3. This ensures that validation errors, required indicators, and values are always perfectly in sync without manual wiring.

### Localization
Validation messages are automatically localized by reading the `LOCALE` cookie (`en` or `es`).
```ts
document.cookie = "LOCALE=es; path=/;";

License

ISC © Juan T