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
Zur Installation springen Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/DimitriGilbert/Formedible --skill formedibleDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
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
;
## 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 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 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) 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 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 import React from "react" ;
const schema = z.object ({
email : z.string ().email (),
});
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!" );
}, []);
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 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 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!)
<form>
<input name ="name" />
<input name ="email" />
</form>
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!
1. Always Use className on Form <Form className="space-y-4" />
2. Toast Notifications import { toast } from "sonner" ;
onSubmit : async ({ value }) => {
toast.success ("Success!" , {
description : "Your data was saved" ,
});
}
3. Use as const for Enums defaultValues : {
plan : "basic" as const ,
role : "admin" as const ,
}
4. Dynamic Options = Function
options : [{ value : "a" , label : "A" }]
options : (values ) => {
if (values.category === "tech" ) return techOptions;
return [];
}
5. Conditional Returns Boolean
conditional : (values ) => {
if (values.type === "business" ) return true ;
}
conditional : (values ) => values.type === "business"
6. Analytics Must Be Memoized const callback = React .useCallback ((... ) => { ... }, []);
const analytics = React .useMemo (() => ({ callback }), [callback]);
Build Workflow (CRITICAL!) PACKAGES ARE SOURCE OF TRUTH
Edit: packages/formedible/src/...
Build: npm run build:pkg
Sync: node scripts/quick-sync.js
Build web: npm run build:web
Sync components: npm run sync-components
Build web: npm run build:web
NEVER edit web app files directly!
Common Issues Issue Fix Field not showing Check field type in field-registry.tsx Dynamic options not updating Use function: options: (values) => {...} Validation not showing Schema names must match field names exactly Conditional always hidden Return boolean, never undefined Analytics not firing Use React.useCallback + React.useMemo Pages not working Pages start at 1, must be sequential
Type Safety const schema = z.object ({
name : z.string (),
age : z.number (),
});
type FormValues = z.infer <typeof schema>;
const { Form } = useFormedible<FormValues >({
schema,
formOptions : {
defaultValues : {
name : "" ,
age : 0 ,
},
},
});
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
Adding New Field Types
Create: packages/formedible/src/components/formedible/fields/my-field.tsx
Use BaseFieldWrapper for consistency
Add type to packages/formedible/src/lib/formedible/types.ts
Register in packages/formedible/src/lib/formedible/field-registry.tsx
Add to packages/formedible/registry.json