Skip to content

Formedible Docs

A clear docs map for forms defined in your code.

Start with the current app-local hook, choose a route by job-to-be-done, and move from field config to rendered examples, builders, parser workflows, and API details.

22+

field types

Rendered through copied shadcn-style components.

0

package lock-in

Consumer imports stay inside the app.

1

field model

Manual, builder, and AI flows share the same config.

Clean-room shadcn install surface

The docs describe what exists in this TanStack Start app.

Formedible is documented here as copied source: the hook, field components, layouts, builder, and AI builder are imported from consumer paths. That keeps customization explicit and avoids runtime references to old package documentation.

Install the registry item

Add the built registry item to your app, then import the copied hook from your local UI package.

pnpm dlx shadcn@latest add https://formedible.dev/r/formedible-core.json

Content model

One typed configuration powers every rendering mode.

Fields stay declarative while TanStack Form owns state and validation. The same model covers single-page forms, page flows, tabs, arrays, dynamic options, persistence, and analytics callbacks.

Typed fields over TanStack Form

Formedible renders shadcn-compatible fields while keeping TanStack Form in reach.

import { z } from 'zod';

import { useFormedible } from '@/components/ui/formedible/hooks/use-formedible';

const onboardingSchema = z.object({
  name: z.string().min(2),
  plan: z.enum(['starter', 'team', 'enterprise']),
  needsMigration: z.boolean(),
});

type OnboardingValues = z.infer<typeof onboardingSchema>;

type WorkspaceRecord = OnboardingValues & {
  slug: string;
};

const workspaceRecords: WorkspaceRecord[] = [];

function createWorkspace(values: OnboardingValues): WorkspaceRecord {
  const workspace = {
    ...values,
    slug: values.name.trim().toLowerCase().replace(/\s+/g, '-'),
  };

  workspaceRecords.push(workspace);
  return workspace;
}

export function OnboardingForm() {
  const { Form } = useFormedible<OnboardingValues>({
    fields: [
      { name: 'name', type: 'text', label: 'Workspace name', required: true },
      { name: 'plan', type: 'radio', label: 'Plan', options: ['starter', 'team', 'enterprise'] },
      { name: 'needsMigration', type: 'switch', label: 'Import an existing form system?' },
    ],
    schema: onboardingSchema,
    formOptions: {
      defaultValues: { name: '', plan: 'team', needsMigration: false },
      onSubmit: ({ value }) => {
        createWorkspace(value);
      },
    },
  });

  return <Form />;
}

Customize the copied registry

The registry is copied into your app, so customize supported field types by editing the local mapping directly.

import type { ReactNode } from 'react';

import { NumberField } from '@/components/ui/formedible/fields/number-field';
import { TextField } from '@/components/ui/formedible/fields/text-field';
import type { FormedibleFieldRenderProps, FormedibleFormValues, NormalizedFieldType } from '@/components/ui/formedible/lib/types';

type FieldComponent = <TFormValues extends FormedibleFormValues>(props: FormedibleFieldRenderProps<TFormValues>) => ReactNode;

const fieldRegistry: Partial<Record<NormalizedFieldType, FieldComponent>> = {
  number: NumberField,
  text: TextField,
};

export function getFieldComponent<TFormValues extends FormedibleFormValues>(
  type: NormalizedFieldType,
): (props: FormedibleFieldRenderProps<TFormValues>) => ReactNode {
  return fieldRegistry[type] ?? TextField;
}

Builder integration

The builder is a consumer component, not a separate docs artifact.

Mount the builder from the app path, let users assemble fields visually, then keep generated configuration reviewable before it is pasted into production code.

Builder shell path

The builder installs into your shadcn UI path, so you can place it beside your own navigation and persistence code.

import { FormBuilder } from '@/components/ui/formedible/builder/form-builder';

export function BuilderWorkspace() {
  return (
    <section aria-labelledby="builder-title" className="grid gap-6">
      <div>
        <p className="text-sm uppercase tracking-[0.24em] text-muted-foreground">Builder</p>
        <h1 id="builder-title" className="text-3xl font-semibold tracking-tight">
          Compose fields, preview output, copy configuration.
        </h1>
      </div>
      <FormBuilder />
    </section>
  );
}

AI-assisted generation

Prompted form generation still lands on the same source model.

Provider selection, chat, generated preview, and final renderer all live in the app. AI can accelerate drafting without creating a second incompatible form format.

AI Builder shell path

AI generation returns the same field model used by hand-written forms.

import { useState } from 'react';

import { AIBuilder } from '@/components/ui/formedible/ai/ai-builder';
import { createDefaultProviderSecrets, createDefaultProviderSettings, ProviderSelection } from '@/components/ui/formedible/ai/provider-selection';

export function AiBuilderWorkspace() {
  const [providerSettings, setProviderSettings] = useState(() => createDefaultProviderSettings('openrouter'));
  const [providerSecrets, setProviderSecrets] = useState(() => createDefaultProviderSecrets('openrouter'));

  return (
    <main className="grid gap-8 lg:grid-cols-[22rem_1fr]">
      <ProviderSelection settings={providerSettings} secrets={providerSecrets} onChange={(settings, secrets) => {
        setProviderSettings(settings);
        setProviderSecrets(secrets);
      }} />
      <AIBuilder
        providerSettings={providerSettings}
        providerSecrets={providerSecrets}
        onProviderSettingsChange={setProviderSettings}
        onProviderSecretsChange={setProviderSecrets}
      />
    </main>
  );
}

Formedible

TanStack Form, shadcn/ui, Zod