formedible

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Formedible Skill

Formedible 使用指南

Formedible is a DECLARATIVE and SCHEMA-DRIVEN form library.
The schema is the single source of truth. Define forms via
fields
array configuration, NOT manual JSX.
Use this skill when working with Formedible forms - creating, debugging, or extending functionality.
Formedible 是一个声明式、基于Schema的表单库。
Schema是唯一的事实来源。通过
fields
数组配置来定义表单,而非手动编写JSX。
在创建、调试或扩展Formedible表单功能时,可参考本指南。

Quick Start

快速开始

⚠️ Formedible is DECLARATIVE and SCHEMA-DRIVEN. Do NOT write manual JSX for fields! import { useFormedible } from "@/hooks/use-formedible"; import { z } from "zod"; import { toast } from "sonner";
const schema = z.object({ name: z.string().min(2), email: z.string().email(), });
const { Form } = useFormedible({ schema, fields: [ { name: "name", type: "text", label: "Name" }, { name: "email", type: "email", label: "Email" }, ], formOptions: { defaultValues: { name: "", email: "" }, onSubmit: async ({ value }) => { toast.success("Form submitted!"); console.log(value); }, }, });
return <Form className="space-y-4" />;
undefined
⚠️ Formedible 是声明式、基于Schema的库。请勿手动为字段编写JSX!
tsx
import { useFormedible } from "@/hooks/use-formedible";
import { z } from "zod";
import { toast } from "sonner";

const schema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "name", type: "text", label: "Name" },
    { name: "email", type: "email", label: "Email" },
  ],
  formOptions: {
    defaultValues: { name: "", email: "" },
    onSubmit: async ({ value }) => {
      toast.success("Form submitted!");
      console.log(value);
    },
  },
});

return <Form className="space-y-4" />;

Field Types Quick Reference

字段类型速查

TypeKey Config
text
Basic text input
email
Email validation
password
Password field
textarea
textareaConfig: { rows, showWordCount, maxLength }
number
min, max, step
date
dateConfig: { disablePastDates, disableFutureDates }
select
options: []
(static or function)
radio
options: []
multiSelect
multiSelectConfig: { maxSelections, searchable }
checkbox
Boolean checkbox
switch
Toggle switch
rating
ratingConfig: { max, allowHalf, icon }
phone
phoneConfig: { format, defaultCountry }
array
arrayConfig: { itemType, minItems, maxItems, sortable, objectConfig }
类型关键配置
text
基础文本输入框
email
邮箱格式验证
password
密码输入框
textarea
textareaConfig: { rows, showWordCount, maxLength }
number
min, max, step
date
dateConfig: { disablePastDates, disableFutureDates }
select
options: []
(静态数组或函数)
radio
options: []
multiSelect
multiSelectConfig: { maxSelections, searchable }
checkbox
布尔值复选框
switch
开关切换组件
rating
ratingConfig: { max, allowHalf, icon }
phone
phoneConfig: { format, defaultCountry }
array
arrayConfig: { itemType, minItems, maxItems, sortable, objectConfig }

Key Examples (Self-Contained)

核心示例(独立可运行)

Multi-Page Form with Dynamic Text

带动态文本的多页表单

tsx
import { useFormedible } from "@/hooks/use-formedible";
import { z } from "zod";
import { toast } from "sonner";

const schema = z.object({
  firstName: z.string().min(1),
  lastName: z.string().min(1),
  email: z.string().email(),
  plan: z.enum(["basic", "pro"]),
});

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "firstName", type: "text", label: "First Name", page: 1 },
    { name: "lastName", type: "text", label: "Last Name", page: 1 },
    {
      name: "email",
      type: "email",
      label: "Email",
      page: 2,
      description: "We'll contact {{firstName}} at {{email}}", // Dynamic text!
    },
    {
      name: "plan",
      type: "radio",
      label: "Plan",
      page: 2,
      options: [
        { value: "basic", label: "Basic - Free" },
        { value: "pro", label: "Pro - $9/mo" },
      ],
    },
  ],
  pages: [
    { page: 1, title: "Personal Info", description: "Tell us about yourself" },
    { page: 2, title: "Contact", description: "How can we reach you, {{firstName}}?" },
  ],
  progress: { showSteps: true, showPercentage: true },
  formOptions: {
    defaultValues: {
      firstName: "",
      lastName: "",
      email: "",
      plan: "basic" as const,
    },
    onSubmit: async ({ value }) => {
      toast.success("Registered!");
    },
  },
});

return <Form className="space-y-4" />;
tsx
import { useFormedible } from "@/hooks/use-formedible";
import { z } from "zod";
import { toast } from "sonner";

const schema = z.object({
  firstName: z.string().min(1),
  lastName: z.string().min(1),
  email: z.string().email(),
  plan: z.enum(["basic", "pro"]),
});

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "firstName", type: "text", label: "First Name", page: 1 },
    { name: "lastName", type: "text", label: "Last Name", page: 1 },
    {
      name: "email",
      type: "email",
      label: "Email",
      page: 2,
      description: "We'll contact {{firstName}} at {{email}}", // Dynamic text!
    },
    {
      name: "plan",
      type: "radio",
      label: "Plan",
      page: 2,
      options: [
        { value: "basic", label: "Basic - Free" },
        { value: "pro", label: "Pro - $9/mo" },
      ],
    },
  ],
  pages: [
    { page: 1, title: "Personal Info", description: "Tell us about yourself" },
    { page: 2, title: "Contact", description: "How can we reach you, {{firstName}}?" },
  ],
  progress: { showSteps: true, showPercentage: true },
  formOptions: {
    defaultValues: {
      firstName: "",
      lastName: "",
      email: "",
      plan: "basic" as const,
    },
    onSubmit: async ({ value }) => {
      toast.success("Registered!");
    },
  },
});

return <Form className="space-y-4" />;

Conditional Fields AND Pages

条件字段与条件页面

tsx
const schema = z.object({
  applicationType: z.enum(["individual", "business"]),
  firstName: z.string().optional(),
  companyName: z.string().optional(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "applicationType",
      type: "radio",
      label: "Application Type",
      page: 1,
      options: [
        { value: "individual", label: "Individual" },
        { value: "business", label: "Business" },
      ],
    },
    {
      name: "firstName",
      type: "text",
      label: "First Name",
      page: 2,
      conditional: (values: any) => values.applicationType === "individual",
    },
    {
      name: "companyName",
      type: "text",
      label: "Company Name",
      page: 3,
      conditional: (values: any) => values.applicationType === "business",
    },
  ],
  pages: [
    { page: 1, title: "Type" },
    {
      page: 2,
      title: "Personal Info",
      conditional: (values: any) => values.applicationType === "individual",
    },
    {
      page: 3,
      title: "Business Info",
      conditional: (values: any) => values.applicationType === "business",
    },
  ],
  formOptions: {
    defaultValues: {
      applicationType: "individual" as const,
      firstName: "",
      companyName: "",
    },
    onSubmit: async ({ value }) => {
      console.log(value);
    },
  },
});
tsx
const schema = z.object({
  applicationType: z.enum(["individual", "business"]),
  firstName: z.string().optional(),
  companyName: z.string().optional(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "applicationType",
      type: "radio",
      label: "Application Type",
      page: 1,
      options: [
        { value: "individual", label: "Individual" },
        { value: "business", label: "Business" },
      ],
    },
    {
      name: "firstName",
      type: "text",
      label: "First Name",
      page: 2,
      conditional: (values: any) => values.applicationType === "individual",
    },
    {
      name: "companyName",
      type: "text",
      label: "Company Name",
      page: 3,
      conditional: (values: any) => values.applicationType === "business",
    },
  ],
  pages: [
    { page: 1, title: "Type" },
    {
      page: 2,
      title: "Personal Info",
      conditional: (values: any) => values.applicationType === "individual",
    },
    {
      page: 3,
      title: "Business Info",
      conditional: (values: any) => values.applicationType === "business",
    },
  ],
  formOptions: {
    defaultValues: {
      applicationType: "individual" as const,
      firstName: "",
      companyName: "",
    },
    onSubmit: async ({ value }) => {
      console.log(value);
    },
  },
});

Tabbed Form

标签页式表单

tsx
const schema = z.object({
  firstName: z.string(),
  theme: z.enum(["light", "dark"]),
  notifications: z.boolean(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "firstName", type: "text", label: "Name", tab: "personal" },
    {
      name: "theme",
      type: "select",
      label: "Theme",
      tab: "preferences",
      options: [
        { value: "light", label: "Light" },
        { value: "dark", label: "Dark" },
      ],
    },
    {
      name: "notifications",
      type: "switch",
      label: "Enable Notifications",
      tab: "preferences",
    },
  ],
  tabs: [
    { id: "personal", label: "Personal Info", description: "About you" },
    { id: "preferences", label: "Preferences", description: "Settings" },
  ],
  formOptions: {
    defaultValues: {
      firstName: "",
      theme: "light" as const,
      notifications: true,
    },
    onSubmit: async ({ value }) => console.log(value),
  },
});
tsx
const schema = z.object({
  firstName: z.string(),
  theme: z.enum(["light", "dark"]),
  notifications: z.boolean(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "firstName", type: "text", label: "Name", tab: "personal" },
    {
      name: "theme",
      type: "select",
      label: "Theme",
      tab: "preferences",
      options: [
        { value: "light", label: "Light" },
        { value: "dark", label: "Dark" },
      ],
    },
    {
      name: "notifications",
      type: "switch",
      label: "Enable Notifications",
      tab: "preferences",
    },
  ],
  tabs: [
    { id: "personal", label: "Personal Info", description: "About you" },
    { id: "preferences", label: "Preferences", description: "Settings" },
  ],
  formOptions: {
    defaultValues: {
      firstName: "",
      theme: "light" as const,
      notifications: true,
    },
    onSubmit: async ({ value }) => console.log(value),
  },
});

Dynamic Options (Dependent Fields)

动态选项(依赖字段)

tsx
const schema = z.object({
  country: z.string(),
  state: z.string(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "country",
      type: "select",
      label: "Country",
      options: [
        { value: "us", label: "United States" },
        { value: "ca", label: "Canada" },
      ],
    },
    {
      name: "state",
      type: "select",
      label: "State/Province",
      options: (values: any) => {
        if (values.country === "us") {
          return [
            { value: "ca", label: "California" },
            { value: "ny", label: "New York" },
          ];
        }
        if (values.country === "ca") {
          return [
            { value: "on", label: "Ontario" },
            { value: "qc", label: "Quebec" },
          ];
        }
        return [];
      },
    },
  ],
  formOptions: {
    defaultValues: { country: "", state: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});
tsx
const schema = z.object({
  country: z.string(),
  state: z.string(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "country",
      type: "select",
      label: "Country",
      options: [
        { value: "us", label: "United States" },
        { value: "ca", label: "Canada" },
      ],
    },
    {
      name: "state",
      type: "select",
      label: "State/Province",
      options: (values: any) => {
        if (values.country === "us") {
          return [
            { value: "ca", label: "California" },
            { value: "ny", label: "New York" },
          ];
        }
        if (values.country === "ca") {
          return [
            { value: "on", label: "Ontario" },
            { value: "qc", label: "Quebec" },
          ];
        }
        return [];
      },
    },
  ],
  formOptions: {
    defaultValues: { country: "", state: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});

Array Fields with Nested Objects

嵌套对象的数组字段

tsx
const schema = z.object({
  teamMembers: z.array(
    z.object({
      name: z.string().min(1),
      email: z.string().email(),
      role: z.enum(["dev", "design", "pm"]),
    })
  ).min(1),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "teamMembers",
      type: "array",
      label: "Team Members",
      section: {
        title: "Team Composition",
        description: "Add your team",
      },
      arrayConfig: {
        itemType: "object",
        itemLabel: "Team Member",
        minItems: 1,
        maxItems: 10,
        sortable: true,
        addButtonLabel: "Add Member",
        defaultValue: {
          name: "",
          email: "",
          role: "dev",
        },
        objectConfig: {
          fields: [
            { name: "name", type: "text", label: "Name" },
            { name: "email", type: "email", label: "Email" },
            {
              name: "role",
              type: "select",
              label: "Role",
              options: [
                { value: "dev", label: "Developer" },
                { value: "design", label: "Designer" },
                { value: "pm", label: "Product Manager" },
              ],
            },
          ],
        },
      },
    },
  ],
  formOptions: {
    defaultValues: {
      teamMembers: [{ name: "", email: "", role: "dev" as const }],
    },
    onSubmit: async ({ value }) => console.log(value),
  },
});
tsx
const schema = z.object({
  teamMembers: z.array(
    z.object({
      name: z.string().min(1),
      email: z.string().email(),
      role: z.enum(["dev", "design", "pm"]),
    })
  ).min(1),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "teamMembers",
      type: "array",
      label: "Team Members",
      section: {
        title: "Team Composition",
        description: "Add your team",
      },
      arrayConfig: {
        itemType: "object",
        itemLabel: "Team Member",
        minItems: 1,
        maxItems: 10,
        sortable: true,
        addButtonLabel: "Add Member",
        defaultValue: {
          name: "",
          email: "",
          role: "dev",
        },
        objectConfig: {
          fields: [
            { name: "name", type: "text", label: "Name" },
            { name: "email", type: "email", label: "Email" },
            {
              name: "role",
              type: "select",
              label: "Role",
              options: [
                { value: "dev", label: "Developer" },
                { value: "design", label: "Designer" },
                { value: "pm", label: "Product Manager" },
              ],
            },
          ],
        },
      },
    },
  ],
  formOptions: {
    defaultValues: {
      teamMembers: [{ name: "", email: "", role: "dev" as const }],
    },
    onSubmit: async ({ value }) => console.log(value),
  },
});

Analytics with Proper Memoization

带正确记忆化的分析功能

tsx
import React from "react";

const schema = z.object({
  email: z.string().email(),
});

// MUST use useCallback for analytics callbacks!
const onFieldFocus = React.useCallback((fieldName: string, timestamp: number) => {
  console.log(`Field "${fieldName}" focused at`, timestamp);
}, []);

const onFieldBlur = React.useCallback((fieldName: string, timeSpent: number) => {
  console.log(`Field "${fieldName}" completed in ${timeSpent}ms`);
}, []);

const onFormComplete = React.useCallback((timeSpent: number, data: any) => {
  console.log(`Form completed in ${timeSpent}ms`, data);
  toast.success("Form completed!");
}, []);

// MUST useMemo the analytics config
const analyticsConfig = React.useMemo(
  () => ({
    onFieldFocus,
    onFieldBlur,
    onFormComplete,
  }),
  [onFieldFocus, onFieldBlur, onFormComplete]
);

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "email", type: "email", label: "Email" },
  ],
  analytics: analyticsConfig,
  formOptions: {
    defaultValues: { email: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});
tsx
import React from "react";

const schema = z.object({
  email: z.string().email(),
});

// 分析回调必须使用useCallback!
const onFieldFocus = React.useCallback((fieldName: string, timestamp: number) => {
  console.log(`Field "${fieldName}" focused at`, timestamp);
}, []);

const onFieldBlur = React.useCallback((fieldName: string, timeSpent: number) => {
  console.log(`Field "${fieldName}" completed in ${timeSpent}ms`);
}, []);

const onFormComplete = React.useCallback((timeSpent: number, data: any) => {
  console.log(`Form completed in ${timeSpent}ms`, data);
  toast.success("Form completed!");
}, []);

// 分析配置必须使用useMemo
const analyticsConfig = React.useMemo(
  () => ({
    onFieldFocus,
    onFieldBlur,
    onFormComplete,
  }),
  [onFieldFocus, onFieldBlur, onFormComplete]
);

const { Form } = useFormedible({
  schema,
  fields: [
    { name: "email", type: "email", label: "Email" },
  ],
  analytics: analyticsConfig,
  formOptions: {
    defaultValues: { email: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});

Rating Field with Config

带配置的评分字段

tsx
const schema = z.object({
  satisfaction: z.number().min(1).max(5),
  improvements: z.string().optional(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "satisfaction",
      type: "rating",
      label: "How satisfied are you?",
      ratingConfig: {
        max: 5,
        allowHalf: false,
        showValue: true,
      },
    },
    {
      name: "improvements",
      type: "textarea",
      label: "What can we improve?",
      conditional: (values: any) => values.satisfaction < 4,
      textareaConfig: {
        rows: 4,
        showWordCount: true,
        maxLength: 500,
      },
    },
  ],
  formOptions: {
    defaultValues: { satisfaction: 5, improvements: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});
tsx
const schema = z.object({
  satisfaction: z.number().min(1).max(5),
  improvements: z.string().optional(),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "satisfaction",
      type: "rating",
      label: "How satisfied are you?",
      ratingConfig: {
        max: 5,
        allowHalf: false,
        showValue: true,
      },
    },
    {
      name: "improvements",
      type: "textarea",
      label: "What can we improve?",
      conditional: (values: any) => values.satisfaction < 4,
      textareaConfig: {
        rows: 4,
        showWordCount: true,
        maxLength: 500,
      },
    },
  ],
  formOptions: {
    defaultValues: { satisfaction: 5, improvements: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});

Textarea with Configuration

带配置的文本域

tsx
const schema = z.object({
  description: z.string().min(20).max(500),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "description",
      type: "textarea",
      label: "Description",
      textareaConfig: {
        rows: 4,
        showWordCount: true,
        maxLength: 500,
      },
    },
  ],
  formOptions: {
    defaultValues: { description: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});
tsx
const schema = z.object({
  description: z.string().min(20).max(500),
});

const { Form } = useFormedible({
  schema,
  fields: [
    {
      name: "description",
      type: "textarea",
      label: "Description",
      textareaConfig: {
        rows: 4,
        showWordCount: true,
        maxLength: 500,
      },
    },
  ],
  formOptions: {
    defaultValues: { description: "" },
    onSubmit: async ({ value }) => console.log(value),
  },
});

Critical Patterns

核心开发模式

0. FORMEDIBLE IS DECLARATIVE AND SCHEMA DRIVEN (MOST IMPORTANT!)

0. Formedible 是声明式、基于Schema的库(最重要!)

tsx
// ❌ WRONG - Never write manual JSX for fields!
<form>
  <input name="name" />
  <input name="email" />
</form>

// ✅ CORRECT - Define fields declaratively via configuration!
const { Form } = useFormedible({
  schema: z.object({
    name: z.string(),
    email: z.string().email(),
  }),
  fields: [
    { name: "name", type: "text", label: "Name" },
    { name: "email", type: "email", label: "Email" },
  ],
  formOptions: {
    onSubmit: async ({ value }) => console.log(value),
  },
});
THE SCHEMA IS THE SINGLE SOURCE OF TRUTH. Field names in
fields
MUST match schema keys exactly!
tsx
// ❌ WRONG - Never write manual JSX for fields!
<form>
  <input name="name" />
  <input name="email" />
</form>

// ✅ CORRECT - Define fields declaratively via configuration!
const { Form } = useFormedible({
  schema: z.object({
    name: z.string(),
    email: z.string().email(),
  }),
  fields: [
    { name: "name", type: "text", label: "Name" },
    { name: "email", type: "email", label: "Email" },
  ],
  formOptions: {
    onSubmit: async ({ value }) => console.log(value),
  },
});
Schema是唯一的事实来源。
fields
中的字段名称必须与Schema的键完全匹配!

1. Always Use className on Form

1. 始终为Form组件添加className

tsx
<Form className="space-y-4" />
tsx
<Form className="space-y-4" />

2. Toast Notifications

2. 提示通知

tsx
import { toast } from "sonner";

onSubmit: async ({ value }) => {
  toast.success("Success!", {
    description: "Your data was saved",
  });
}
tsx
import { toast } from "sonner";

onSubmit: async ({ value }) => {
  toast.success("Success!", {
    description: "Your data was saved",
  });
}

3. Use
as const
for Enums

3. 为枚举值使用
as const

tsx
defaultValues: {
  plan: "basic" as const,  // ✅
  role: "admin" as const,  // ✅
}
tsx
defaultValues: {
  plan: "basic" as const,  // ✅
  role: "admin" as const,  // ✅
}

4. Dynamic Options = Function

4. 动态选项需使用函数形式

tsx
// ❌ Wrong
options: [{ value: "a", label: "A" }]

// ✅ Correct
options: (values) => {
  if (values.category === "tech") return techOptions;
  return [];
}
tsx
// ❌ Wrong
options: [{ value: "a", label: "A" }]

// ✅ Correct
options: (values) => {
  if (values.category === "tech") return techOptions;
  return [];
}

5. Conditional Returns Boolean

5. 条件判断需返回布尔值

tsx
// ❌ Wrong
conditional: (values) => {
  if (values.type === "business") return true;
}

// ✅ Correct
conditional: (values) => values.type === "business"
tsx
// ❌ Wrong
conditional: (values) => {
  if (values.type === "business") return true;
}

// ✅ Correct
conditional: (values) => values.type === "business"

6. Analytics Must Be Memoized

6. 分析功能必须使用记忆化

tsx
const callback = React.useCallback((...) => { ... }, []);
const analytics = React.useMemo(() => ({ callback }), [callback]);
tsx
const callback = React.useCallback((...) => { ... }, []);
const analytics = React.useMemo(() => ({ callback }), [callback]);

Build Workflow (CRITICAL!)

构建工作流(至关重要!)

PACKAGES ARE SOURCE OF TRUTH
  1. Edit:
    packages/formedible/src/...
  2. Build:
    npm run build:pkg
  3. Sync:
    node scripts/quick-sync.js
  4. Build web:
    npm run build:web
  5. Sync components:
    npm run sync-components
  6. Build web:
    npm run build:web
NEVER edit web app files directly!
包文件是唯一事实来源
  1. 编辑:
    packages/formedible/src/...
  2. 构建:
    npm run build:pkg
  3. 同步:
    node scripts/quick-sync.js
  4. 构建Web应用:
    npm run build:web
  5. 同步组件:
    npm run sync-components
  6. 构建Web应用:
    npm run build:web
切勿直接编辑Web应用文件!

Common Issues

常见问题

IssueFix
Field not showingCheck field type in
field-registry.tsx
Dynamic options not updatingUse function:
options: (values) => {...}
Validation not showingSchema names must match field names exactly
Conditional always hiddenReturn boolean, never undefined
Analytics not firingUse
React.useCallback
+
React.useMemo
Pages not workingPages start at 1, must be sequential
问题解决方法
字段不显示检查
field-registry.tsx
中的字段类型
动态选项不更新使用函数形式:
options: (values) => {...}
验证不生效Schema中的名称必须与字段名称完全匹配
条件字段始终隐藏返回布尔值,切勿返回undefined
分析功能不触发使用
React.useCallback
+
React.useMemo
多页功能失效页码从1开始,且必须连续

Type Safety

类型安全

tsx
const schema = z.object({
  name: z.string(),
  age: z.number(),
});

type FormValues = z.infer<typeof schema>;

const { Form } = useFormedible<FormValues>({
  schema,
  formOptions: {
    defaultValues: {
      name: "",  // Type-safe
      age: 0,    // Type-safe
    },
  },
});
tsx
const schema = z.object({
  name: z.string(),
  age: z.number(),
});

type FormValues = z.infer<typeof schema>;

const { Form } = useFormedible<FormValues>({
  schema,
  formOptions: {
    defaultValues: {
      name: "",  // Type-safe
      age: 0,    // Type-safe
    },
  },
});

File Structure Reference

文件结构参考

packages/formedible/src/
├── hooks/use-formedible.tsx          # Main hook
├── components/formedible/
│   ├── fields/                       # All 22 field components
│   ├── layout/                       # FormGrid, FormTabs, etc.
│   └── ui/                           # Radix UI primitives
├── lib/formedible/
│   ├── types.ts                      # TypeScript interfaces
│   ├── field-registry.tsx            # Field type mapping
│   └── template-interpolation.ts     # Dynamic text resolution
packages/formedible/src/
├── hooks/use-formedible.tsx          # 核心Hook
├── components/formedible/
│   ├── fields/                       # 所有22种字段组件
│   ├── layout/                       # 表单网格、标签页等布局组件
│   └── ui/                           # Radix UI 基础组件
├── lib/formedible/
│   ├── types.ts                      # TypeScript 接口定义
│   ├── field-registry.tsx            # 字段类型映射表
│   └── template-interpolation.ts     # 动态文本解析逻辑

Adding New Field Types

添加新字段类型

  1. Create:
    packages/formedible/src/components/formedible/fields/my-field.tsx
  2. Use
    BaseFieldWrapper
    for consistency
  3. Add type to
    packages/formedible/src/lib/formedible/types.ts
  4. Register in
    packages/formedible/src/lib/formedible/field-registry.tsx
  5. Add to
    packages/formedible/registry.json
See FIELD_TEMPLATES.md for templates.
  1. 创建文件:
    packages/formedible/src/components/formedible/fields/my-field.tsx
  2. 使用
    BaseFieldWrapper
    以保持组件一致性
  3. packages/formedible/src/lib/formedible/types.ts
    中添加类型定义
  4. packages/formedible/src/lib/formedible/field-registry.tsx
    中注册该字段
  5. packages/formedible/registry.json
    中添加配置
请参考FIELD_TEMPLATES.md获取模板。