Installation

How to install and configure @saastro/forms in your React project.

Installation

Learn how to install and configure @saastro/forms in your React project.

Prerequisites

Before you begin, make sure you have:

  • React 18 or later
  • TypeScript 5.0 or later (recommended)
  • A UI component library (we recommend shadcn/ui)

@saastro/forms ships with a CLI that scaffolds example forms and installs everything they need — shadcn components and peer dependencies. The fastest path is three commands:

# 1. Install the package
npm install @saastro/forms

# 2. Generate two example forms (zero-config) in src/components/
npx @saastro/forms init

# 3. Install any missing shadcn components AND peer dependencies
npx @saastro/forms install-deps

That’s it. Open src/components/ExampleForm.tsx to see a FormBuilder example, or src/components/ExampleFormJson.tsx for the equivalent JSON-config form.

Vite only: the scaffolded examples auto-discover components with import.meta.glob, which is a Vite feature — it does not exist in Next.js, webpack, or other bundlers. On non-Vite setups, pass components explicitly via the components prop instead (see Explicit Components below and the Recipes for framework-specific examples).

See the CLI reference for every flag and option.

How init and install-deps work

  • init creates two example files in src/components/:
    • ExampleForm.tsx — programmatic form with FormBuilder
    • ExampleFormJson.tsx — JSON-config form with auto-discovered components
  • install-deps:
    1. Reads your project’s components.json (shadcn config) and runs shadcn@latest add ... for any required components that are missing.
    2. Detects your package manager from the lockfile and installs any missing peer dependencies (zod, react-hook-form, react-day-picker, date-fns).

Both commands need a components.json (shadcn config) in your project root and exit if it’s missing — if you don’t have one yet, run npx shadcn@latest init first.

Use the form in your page

import { ExampleForm } from '@/components/ExampleForm';

export default function ContactPage() {
  return <ExampleForm />;
}

The form auto-discovers shadcn components via Vite’s import.meta.glob. No FormProvider and no barrel file required.


Manual Setup

If you prefer to wire things up by hand, follow these four steps instead of running the CLI.

1. Install the package

npm install @saastro/forms
# or
pnpm add @saastro/forms
# or
bun add @saastro/forms

2. Import the stylesheet

Add this once to your app entry point (e.g. main.tsx, layout.tsx, or globals.css):

import '@saastro/forms/styles.css'

This provides the selected-state styles for button groups (button-radio, button-checkbox, button-card). It uses CSS custom properties from your design system — no Tailwind compilation needed.

Server Components / SSR: @saastro/forms is a client-side library (hooks, state, events) but is safe to import on the server — all window/document access is guarded. In React Server Components setups (Next.js App Router), render <Form> inside a 'use client' boundary.

3. Install peer dependencies

@saastro/forms requires React Hook Form, Zod, date-fns, and react-day-picker:

npm install react-hook-form zod date-fns react-day-picker

3. Install shadcn/ui components

Install only the components you need. For a basic text form:

npx shadcn@latest add input button label field form

For all available field types:

npx shadcn@latest add input button label textarea select checkbox radio-group popover calendar tooltip slider switch separator dialog command input-otp native-select field form

4. Create your first form (Zero-Config)

If your project uses Vite, auto-discover all your shadcn components with import.meta.glob:

import { Form, FormBuilder } from '@saastro/forms';

const uiComponents = import.meta.glob('@/components/ui/*.tsx', { eager: true });

const config = FormBuilder.create('contact')
  .addField('name', (f) => f.type('text').label('Name').required().minLength(2))
  .addField('email', (f) => f.type('email').label('Email').required().email())
  .addStep('main', ['name', 'email'])
  .buttons({ submit: { type: 'submit', label: 'Send' } })
  .build();

export function ContactForm() {
  return (
    <Form
      config={config}
      components={uiComponents}
      onSubmit={(values) => console.log('Submitted:', values)}
    />
  );
}

No FormProvider, no barrel file, no manual component registration. The Form auto-discovers components by parsing the glob result.

Not using Vite? import.meta.glob is Vite-specific. On Next.js, webpack, or any other bundler, skip this step and pass the components explicitly — the next section shows how, and it works everywhere.


Alternative: Explicit Components

If you’re not using Vite, prefer explicit imports, or need to override specific components, pass them directly to the components prop. This works with every bundler:

import { Form, FormBuilder } from '@saastro/forms';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Field, FieldLabel, FieldDescription, FieldError } from '@/components/ui/field';
import { FormField, FormControl } from '@/components/ui/form';

<Form
  config={config}
  components={{
    Input,
    Button,
    Label,
    Field,
    FieldLabel,
    FieldDescription,
    FieldError,
    FormField,
    FormControl,
  }}
/>;

Provider Mode (Legacy)

For apps with many forms sharing the same components, you can use the provider pattern:

import { Form, FormBuilder, ComponentProvider, createComponentRegistry } from '@saastro/forms';
import * as shadcn from '@/lib/form-components'; // Your barrel file

const registry = createComponentRegistry(shadcn);

export function App() {
  return (
    <ComponentProvider components={registry}>
      <ContactForm />
      <SignupForm />
      <CheckoutForm />
    </ComponentProvider>
  );
}

function ContactForm() {
  const config = FormBuilder.create('contact')
    .addField('name', (f) => f.type('text').label('Name').required())
    .addStep('main', ['name'])
    .build();

  return <Form config={config} />;
}

Benefits of Zero-Config

Zero-config mode requires Vite, but when available it’s the least amount of code:

  1. One line of setup — just import.meta.glob and you’re done
  2. Auto-discovers components — no manual registration needed
  3. Missing component fallback — shows helpful warnings with the install command
  4. No Provider boilerplate — just render <Form />
  5. Override per-form — to replace a discovered component, convert the glob with parseGlobModules (exported from @saastro/forms) and spread your override over the result: components={{ ...parseGlobModules(modules), Input: MyInput }}

Required Components by Field Type

When a component is missing, you’ll see a helpful warning with the recommended fix: npx @saastro/forms install-deps.

Field TypeRequired Components
text, email, telInput, Label, Field, FieldLabel, FieldDescription, FieldError, FormField, FormControl
textareaTextarea, Label, Field, FieldLabel, FieldDescription, FieldError, FormField, FormControl
selectSelect, SelectTrigger, SelectContent, SelectItem, SelectValue, Field, …
checkbox, switchCheckbox/Switch, Label, Field, …
dateCalendar, Popover, PopoverTrigger, PopoverContent, Button, Field, …
comboboxCommand, CommandInput, CommandList, Popover, Button, Field, …

Check Required Components Programmatically

import { getRequiredComponents, getInstallCommand } from '@saastro/forms';

const required = getRequiredComponents(config);
// ['Input', 'Button', 'Label', 'Field', ...]

const installCmd = getInstallCommand(required);
// 'npx shadcn@latest add input button label field form'

Tailwind CSS 4

If you’re using Tailwind CSS 4, add the following @source directive to your main CSS file so Tailwind can detect the classes used by this package:

@source "node_modules/@saastro/forms/dist/**/*.js";

TypeScript Configuration

@saastro/forms itself has no special TypeScript requirements. The settings below are what the examples in these docs (and the CLI-generated files) assume:

{
  "compilerOptions": {
    "strict": true,
    "moduleResolution": "bundler",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

The @/* paths alias is the standard shadcn/ui convention — it’s where the examples import UI components from (@/components/ui/...). Adjust it to match your project layout if yours differs.


Next Steps

  • Read the CLI reference for every CLI command and flag
  • Learn the FormBuilder API — the recommended way to build forms
  • Set up Validation — Zod schemas or declarative rules
  • Build Multi-Step Forms — wizards with conditional routing
  • Browse the Field Types to see all available fields
  • Check the Recipes for framework-specific setups (Next.js and other non-Vite bundlers)