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

@spacedrive/forms

v0.2.3

Published

Form field wrappers for SpaceUI built on react-hook-form

Readme

@spacedrive/forms

Form field wrappers built on react-hook-form for SpaceUI.

Installation

bun add @spacedrive/forms @spacedrive/primitives react-hook-form zod
# or
npm install @spacedrive/forms @spacedrive/primitives react-hook-form zod

Peer dependencies:

  • react ^18.0.0 || ^19.0.0
  • react-dom ^18.0.0 || ^19.0.0
  • react-hook-form ^7.0.0
  • zod ^3.0.0

Usage

Basic Form

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { Form, InputField, SelectField, CheckboxField } from '@spacedrive/forms';

const schema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email'),
  role: z.enum(['admin', 'user']),
  newsletter: z.boolean().default(false),
});

type FormData = z.infer<typeof schema>;

function MyForm() {
  const form = useForm<FormData>({
    resolver: zodResolver(schema),
    defaultValues: {
      role: 'user',
      newsletter: false,
    },
  });

  const onSubmit = (data: FormData) => {
    console.log(data);
  };

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <InputField
          name="name"
          label="Full Name"
          placeholder="John Doe"
        />
        
        <InputField
          name="email"
          label="Email"
          type="email"
          placeholder="[email protected]"
        />
        
        <SelectField
          name="role"
          label="Role"
          options={[
            { value: 'admin', label: 'Administrator' },
            { value: 'user', label: 'User' },
          ]}
        />
        
        <CheckboxField
          name="newsletter"
          label="Subscribe to newsletter"
        />
        
        <button type="submit">Submit</button>
      </form>
    </Form>
  );
}

Components

Form

The root form provider that wraps react-hook-form's FormProvider:

<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)}>
    {/* Fields */}
  </form>
</Form>

InputField

Text input with label, placeholder, and error handling:

<InputField
  name="username"
  label="Username"
  description="Your public display name"
  placeholder="johndoe"
  type="text"  // text, email, password, etc.
/>

TextAreaField

Multi-line text input:

<TextAreaField
  name="bio"
  label="Biography"
  placeholder="Tell us about yourself..."
  rows={4}
/>

SelectField

Dropdown select with options:

<SelectField
  name="country"
  label="Country"
  placeholder="Select a country"
  options={[
    { value: 'us', label: 'United States' },
    { value: 'uk', label: 'United Kingdom' },
    { value: 'ca', label: 'Canada' },
  ]}
/>

CheckboxField

Single checkbox with label:

<CheckboxField
  name="agree"
  label="I agree to the terms"
  description="You must agree to continue"
/>

RadioGroupField

Radio button group:

<RadioGroupField
  name="plan"
  label="Subscription Plan"
  options={[
    { value: 'free', label: 'Free' },
    { value: 'pro', label: 'Pro ($10/month)' },
    { value: 'enterprise', label: 'Enterprise ($50/month)' },
  ]}
/>

SwitchField

Toggle switch:

<SwitchField
  name="notifications"
  label="Enable notifications"
  description="Receive push notifications"
/>

Features

  • Automatic validation - Zod schema integration
  • Error messages - Displays validation errors
  • Accessible - Proper labels and ARIA attributes
  • Type-safe - Full TypeScript support
  • Composable - Works with react-hook-form's API

Advanced Usage

Custom Validation

import { z } from 'zod';

const schema = z.object({
  password: z.string().min(8),
  confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ['confirmPassword'],
});

Dynamic Fields

import { useFieldArray } from 'react-hook-form';

function DynamicForm() {
  const { fields, append, remove } = useFieldArray({
    control: form.control,
    name: 'items',
  });

  return (
    <>
      {fields.map((field, index) => (
        <InputField
          key={field.id}
          name={`items.${index}.name`}
          label={`Item ${index + 1}`}
        />
      ))}
      <button type="button" onClick={() => append({ name: '' })}>
        Add Item
      </button>
    </>
  );
}

Manual Error Display

import { FormMessage } from '@spacedrive/forms';

// Inside a custom component
<FormMessage>
  Custom error message here
</FormMessage>

Styling

Form fields automatically use SpaceUI's semantic colors:

  • Labels: text-ink
  • Descriptions: text-ink-dull
  • Errors: text-status-error
  • Inputs: border-app-line bg-app-box

No additional styling needed - works out of the box with your Tailwind + SpaceUI setup.

API Reference

Form Components

All field components accept:

| Prop | Type | Description | |------|------|-------------| | name | string | Field name (required) | | label | string | Field label | | description | string | Help text below field | | disabled | boolean | Disable the field |

SelectField Options

type SelectOption = {
  value: string;
  label: string;
};

RadioGroupField Options

type RadioOption = {
  value: string;
  label: string;
};

License

MIT © Spacedrive