Skip to main content

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에서 보기