formedible
Expert knowledge for Formedible - A React form library built on TanStack Form with 22+ field types, multi-page forms, analytics, and type-safe validation
소스 정보
- 저장소
- DimitriGilbert/Formedible
- 최근 소스 활동
- 2026년 2월 8일 23:13
- 감지된 SKILL.md 언어
- 영어
- 스타
- 47
- 포크
- 2
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
파일 탐색기
7 개 파일SKILL.md 표시 중
SKILL.md
소스 지침 · 읽기 전용 미리보기- name
- formedible
- description
- Expert knowledge for Formedible - A React form library built on TanStack Form with 22+ field types, multi-page forms, analytics, and type-safe validation
# Formedible Skill
**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.
## 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" />;
```
## Field Types Quick Reference
| Type | Key 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 }` |
## 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" />;
```
### 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);
},
},
});
```
### 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),
},
});
```
### 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),
},
});
```
### 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),
},
});
```
### 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,
GitHub에서 보기이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기