Form field wrappers built on react-hook-form for SpaceUI.
bun add @spacedrive/forms @spacedrive/primitives react-hook-form zod
# or
npm install @spacedrive/forms @spacedrive/primitives react-hook-form zodPeer dependencies:
react^18.0.0 || ^19.0.0react-dom^18.0.0 || ^19.0.0react-hook-form^7.0.0zod^3.0.0
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="john@example.com"
/>
<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>
);
}The root form provider that wraps react-hook-form's FormProvider:
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* Fields */}
</form>
</Form>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.
/>Multi-line text input:
<TextAreaField
name="bio"
label="Biography"
placeholder="Tell us about yourself..."
rows={4}
/>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' },
]}
/>Single checkbox with label:
<CheckboxField
name="agree"
label="I agree to the terms"
description="You must agree to continue"
/>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)' },
]}
/>Toggle switch:
<SwitchField
name="notifications"
label="Enable notifications"
description="Receive push notifications"
/>- 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
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'],
});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>
</>
);
}import { FormMessage } from '@spacedrive/forms';
// Inside a custom component
<FormMessage>
Custom error message here
</FormMessage>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.
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 |
type SelectOption = {
value: string;
label: string;
};type RadioOption = {
value: string;
label: string;
};MIT © Spacedrive