@primereact/mcp 11.0.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/llms/headless/components/sidebar/api.json +1029 -54
- package/data/llms/headless/guides/misc/internationalization.md +282 -0
- package/data/llms/headless/guides/misc/locale.md +280 -0
- package/data/llms/llms-full.txt +9822 -1377
- package/data/llms/llms.txt +15 -0
- package/data/llms/primitive/components/sidebar/api.json +127 -33
- package/data/llms/primitive/guides/migration/updating-to-v11.md +2219 -0
- package/data/llms/primitive/guides/misc/internationalization.md +287 -0
- package/data/llms/primitive/guides/misc/locale.md +280 -0
- package/data/llms/styled/add-ons/designer/ci.md +273 -0
- package/data/llms/styled/add-ons/designer/guide.md +99 -0
- package/data/llms/styled/add-ons/designer/overview.md +194 -0
- package/data/llms/styled/add-ons/uikit/guide/v3.md +181 -0
- package/data/llms/styled/add-ons/uikit/guide/v4.md +163 -0
- package/data/llms/styled/add-ons/uikit/overview.md +204 -0
- package/data/llms/styled/components/datatable.md +8 -5
- package/data/llms/styled/components/menu.md +39 -41
- package/data/llms/styled/guides/configuration.md +198 -0
- package/data/llms/styled/guides/form/formik.md +448 -0
- package/data/llms/styled/guides/form/react-hook-form.md +503 -0
- package/data/llms/styled/guides/form/tanstack.md +502 -0
- package/data/llms/styled/guides/migration/updating-to-v11.md +2219 -0
- package/data/llms/styled/guides/misc/internationalization.md +379 -0
- package/data/llms/styled/guides/misc/locale.md +280 -0
- package/data/llms/tailwind/components/menu.md +5 -5
- package/data/llms/tailwind/components/tooltip.md +4 -13
- package/data/llms/tailwind/guides/misc/internationalization.md +287 -0
- package/data/llms/tailwind/guides/misc/locale.md +280 -0
- package/data/manifest.json +339 -24
- package/data/mcp-data.json +263 -8
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
# Formik
|
|
2
|
+
|
|
3
|
+
Build validated forms with PrimeReact and Formik.
|
|
4
|
+
|
|
5
|
+
<DocMessage>
|
|
6
|
+
Open the <a href="https://stackblitz.com/edit/vitejs-vite-ptxsdm4s?file=src%2Fcomponents%2Fformik-form.tsx" target="_blank" rel="noopener noreferrer">complete example on StackBlitz</a> to run and edit the form built in this guide.
|
|
7
|
+
</DocMessage>
|
|
8
|
+
|
|
9
|
+
## Introduction
|
|
10
|
+
|
|
11
|
+
[Formik](https://formik.org/) centralizes form state, validation and submission in a single `useFormik` hook. PrimeReact inputs are controlled, so they read their value from `formik.values` and report changes back through `getFieldProps` or `setFieldValue`.
|
|
12
|
+
|
|
13
|
+
Throughout this guide validation is handled with [Zod](https://zod.dev/) through the [`zod-formik-adapter`](https://www.npmjs.com/package/zod-formik-adapter): you describe the form once as a schema and Formik enforces it.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install formik zod zod-formik-adapter
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Anatomy
|
|
20
|
+
|
|
21
|
+
A native field binds with `getFieldProps(name)`, which returns the `name`, `value`, `onChange` and `onBlur` that Formik needs. Errors live in `formik.errors`, and `formik.touched` tells you whether the user has visited the field yet:
|
|
22
|
+
|
|
23
|
+
```tsx title="Anatomy" showLineNumbers
|
|
24
|
+
<InputText
|
|
25
|
+
{...formik.getFieldProps('projectName')} // name, value, onChange, onBlur
|
|
26
|
+
invalid={formik.touched.projectName && !!formik.errors.projectName} // error styling
|
|
27
|
+
/>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **`getFieldProps(name)`**: bundles the handlers that bind a native input to the form.
|
|
31
|
+
- **`formik.values`**: the current value of every field.
|
|
32
|
+
- **`formik.errors` / `formik.touched`**: the validation message and visited state per field.
|
|
33
|
+
|
|
34
|
+
`getFieldProps` is enough for native inputs like `InputText` and `Textarea`. Composite components report through their own callback, which is covered in [Fields](#fields).
|
|
35
|
+
|
|
36
|
+
## Create the form
|
|
37
|
+
|
|
38
|
+
### Schema
|
|
39
|
+
|
|
40
|
+
Describe the shape and rules of the form with Zod. The message passed to each rule is what surfaces as the field error:
|
|
41
|
+
|
|
42
|
+
```tsx title="schema.ts" showLineNumbers
|
|
43
|
+
import { z } from 'zod';
|
|
44
|
+
|
|
45
|
+
export const schema = z.object({
|
|
46
|
+
projectName: z.string().min(1, 'Project name is required.'),
|
|
47
|
+
framework: z.string().min(1, 'Please select a framework.'),
|
|
48
|
+
rootDir: z.string().min(1, 'Root directory is required.'),
|
|
49
|
+
environments: z.array(z.string()).min(1, 'Select at least one environment.')
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export type FormValues = z.infer<typeof schema>;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Connect to Formik
|
|
56
|
+
|
|
57
|
+
Pass the schema through `toFormikValidationSchema`, and provide `initialValues` for every field so the inputs are controlled from the first render:
|
|
58
|
+
|
|
59
|
+
```tsx title="deploy-form.tsx" showLineNumbers {8}
|
|
60
|
+
import { useFormik } from 'formik';
|
|
61
|
+
import { toFormikValidationSchema } from 'zod-formik-adapter';
|
|
62
|
+
import { schema, type FormValues } from './schema';
|
|
63
|
+
|
|
64
|
+
const formik = useFormik<FormValues>({
|
|
65
|
+
initialValues: { projectName: '', framework: '', rootDir: './', environments: [] },
|
|
66
|
+
validationSchema: toFormikValidationSchema(schema),
|
|
67
|
+
onSubmit: (values) => {
|
|
68
|
+
console.log(values);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
<DocMessage>
|
|
74
|
+
Formik validates with Yup natively. The `zod-formik-adapter` lets you keep a single Zod schema shared with the rest of your app; swap it for `validationSchema: yupSchema` if you prefer Yup.
|
|
75
|
+
</DocMessage>
|
|
76
|
+
|
|
77
|
+
### Submit
|
|
78
|
+
|
|
79
|
+
Wire the form's `onSubmit` to `formik.handleSubmit`. Formik validates first and only calls your `onSubmit` callback when every field passes:
|
|
80
|
+
|
|
81
|
+
```tsx title="Submit" showLineNumbers
|
|
82
|
+
return (
|
|
83
|
+
<form onSubmit={formik.handleSubmit} className="flex flex-col gap-6">
|
|
84
|
+
{/* fields go here */}
|
|
85
|
+
<Button type="submit">Deploy</Button>
|
|
86
|
+
</form>
|
|
87
|
+
);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Reset
|
|
91
|
+
|
|
92
|
+
Call `formik.resetForm()` to clear every field back to `initialValues`; pass an object to seed the form with new values instead, for example after a successful submit:
|
|
93
|
+
|
|
94
|
+
```tsx title="Reset"
|
|
95
|
+
// restore the initial values
|
|
96
|
+
<Button type="button" severity="secondary" variant="outlined" onClick={() => formik.resetForm()}>
|
|
97
|
+
Reset
|
|
98
|
+
</Button>;
|
|
99
|
+
|
|
100
|
+
// or set new values
|
|
101
|
+
formik.resetForm({ values: { projectName: 'my-app', framework: 'nextjs', rootDir: './', environments: ['production'] } });
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Result
|
|
105
|
+
|
|
106
|
+
Everything above comes together as the complete form. Each field is broken down in [Fields](#fields):
|
|
107
|
+
|
|
108
|
+
<div className="[&_pre]:max-h-[32rem] [&_pre]:overflow-y-auto">
|
|
109
|
+
|
|
110
|
+
```tsx title="deploy-form.tsx" showLineNumbers
|
|
111
|
+
'use client';
|
|
112
|
+
import { useFormik } from 'formik';
|
|
113
|
+
import { toFormikValidationSchema } from 'zod-formik-adapter';
|
|
114
|
+
import { z } from 'zod';
|
|
115
|
+
import { ChevronDown, Check } from '@primeicons/react';
|
|
116
|
+
import { InputText } from '@primereact/ui/inputtext';
|
|
117
|
+
import { Select } from '@primereact/ui/select';
|
|
118
|
+
import { Checkbox } from '@primereact/ui/checkbox';
|
|
119
|
+
import { CheckboxGroup } from '@primereact/ui/checkboxgroup';
|
|
120
|
+
import { Label } from '@primereact/ui/label';
|
|
121
|
+
import { Button } from '@primereact/ui/button';
|
|
122
|
+
import { Message } from '@primereact/ui/message';
|
|
123
|
+
|
|
124
|
+
const FRAMEWORKS = [
|
|
125
|
+
{ label: 'Next.js', value: 'nextjs' },
|
|
126
|
+
{ label: 'Vite', value: 'vite' },
|
|
127
|
+
{ label: 'Astro', value: 'astro' }
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const ENVIRONMENTS = [
|
|
131
|
+
{ label: 'Production', value: 'production' },
|
|
132
|
+
{ label: 'Preview', value: 'preview' },
|
|
133
|
+
{ label: 'Development', value: 'development' }
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
const schema = z.object({
|
|
137
|
+
projectName: z.string().min(1, 'Project name is required.'),
|
|
138
|
+
framework: z.string().min(1, 'Please select a framework.'),
|
|
139
|
+
rootDir: z.string().min(1, 'Root directory is required.'),
|
|
140
|
+
environments: z.array(z.string()).min(1, 'Select at least one environment.')
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
type FormValues = z.infer<typeof schema>;
|
|
144
|
+
|
|
145
|
+
export default function DeployForm() {
|
|
146
|
+
const formik = useFormik<FormValues>({
|
|
147
|
+
initialValues: { projectName: '', framework: '', rootDir: './', environments: [] },
|
|
148
|
+
validationSchema: toFormikValidationSchema(schema),
|
|
149
|
+
onSubmit: (values) => {
|
|
150
|
+
console.log(values);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<form onSubmit={formik.handleSubmit} className="flex flex-col gap-6">
|
|
156
|
+
<div className="flex flex-col gap-2">
|
|
157
|
+
<Label htmlFor="projectName">Project name</Label>
|
|
158
|
+
<InputText id="projectName" {...formik.getFieldProps('projectName')} invalid={formik.touched.projectName && !!formik.errors.projectName} fluid />
|
|
159
|
+
{formik.touched.projectName && formik.errors.projectName && (
|
|
160
|
+
<Message.Root severity="error" variant="simple" size="small">
|
|
161
|
+
<Message.Content>
|
|
162
|
+
<Message.Text>{formik.errors.projectName}</Message.Text>
|
|
163
|
+
</Message.Content>
|
|
164
|
+
</Message.Root>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div className="flex flex-col gap-2">
|
|
169
|
+
<Label htmlFor="framework">Framework preset</Label>
|
|
170
|
+
<Select.Root
|
|
171
|
+
value={formik.values.framework}
|
|
172
|
+
onValueChange={(e) => formik.setFieldValue('framework', e.value)}
|
|
173
|
+
onBlur={() => formik.setFieldTouched('framework', true)}
|
|
174
|
+
options={FRAMEWORKS}
|
|
175
|
+
optionLabel="label"
|
|
176
|
+
optionValue="value"
|
|
177
|
+
invalid={formik.touched.framework && !!formik.errors.framework}
|
|
178
|
+
>
|
|
179
|
+
<Select.Trigger type="button">
|
|
180
|
+
<Select.Value placeholder="Select a framework" />
|
|
181
|
+
<Select.Indicator>
|
|
182
|
+
<ChevronDown />
|
|
183
|
+
</Select.Indicator>
|
|
184
|
+
</Select.Trigger>
|
|
185
|
+
<Select.Portal>
|
|
186
|
+
<Select.Positioner>
|
|
187
|
+
<Select.Popup>
|
|
188
|
+
<Select.List />
|
|
189
|
+
</Select.Popup>
|
|
190
|
+
</Select.Positioner>
|
|
191
|
+
</Select.Portal>
|
|
192
|
+
</Select.Root>
|
|
193
|
+
{formik.touched.framework && formik.errors.framework && (
|
|
194
|
+
<Message.Root severity="error" variant="simple" size="small">
|
|
195
|
+
<Message.Content>
|
|
196
|
+
<Message.Text>{formik.errors.framework}</Message.Text>
|
|
197
|
+
</Message.Content>
|
|
198
|
+
</Message.Root>
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div className="flex flex-col gap-2">
|
|
203
|
+
<Label htmlFor="rootDir">Root directory</Label>
|
|
204
|
+
<InputText id="rootDir" {...formik.getFieldProps('rootDir')} invalid={formik.touched.rootDir && !!formik.errors.rootDir} fluid />
|
|
205
|
+
{formik.touched.rootDir && formik.errors.rootDir && (
|
|
206
|
+
<Message.Root severity="error" variant="simple" size="small">
|
|
207
|
+
<Message.Content>
|
|
208
|
+
<Message.Text>{formik.errors.rootDir}</Message.Text>
|
|
209
|
+
</Message.Content>
|
|
210
|
+
</Message.Root>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<div className="flex flex-col gap-2">
|
|
215
|
+
<Label>Environments</Label>
|
|
216
|
+
<CheckboxGroup value={formik.values.environments} onValueChange={(e) => formik.setFieldValue('environments', e.value)}>
|
|
217
|
+
{ENVIRONMENTS.map((env) => (
|
|
218
|
+
<div key={env.value} className="flex items-center gap-2">
|
|
219
|
+
<Checkbox.Root inputId={env.value} value={env.value}>
|
|
220
|
+
<Checkbox.Box>
|
|
221
|
+
<Checkbox.Indicator match="checked">
|
|
222
|
+
<Check />
|
|
223
|
+
</Checkbox.Indicator>
|
|
224
|
+
</Checkbox.Box>
|
|
225
|
+
</Checkbox.Root>
|
|
226
|
+
<Label htmlFor={env.value}>{env.label}</Label>
|
|
227
|
+
</div>
|
|
228
|
+
))}
|
|
229
|
+
</CheckboxGroup>
|
|
230
|
+
{formik.touched.environments && formik.errors.environments && (
|
|
231
|
+
<Message.Root severity="error" variant="simple" size="small">
|
|
232
|
+
<Message.Content>
|
|
233
|
+
<Message.Text>{formik.errors.environments as string}</Message.Text>
|
|
234
|
+
</Message.Content>
|
|
235
|
+
</Message.Root>
|
|
236
|
+
)}
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div className="flex justify-end gap-3">
|
|
240
|
+
<Button type="button" severity="secondary" variant="outlined" onClick={() => formik.resetForm()}>
|
|
241
|
+
Reset
|
|
242
|
+
</Button>
|
|
243
|
+
<Button type="submit">Deploy</Button>
|
|
244
|
+
</div>
|
|
245
|
+
</form>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
## Fields
|
|
253
|
+
|
|
254
|
+
Every field reads from `formik.values`. How you forward the change depends on the component:
|
|
255
|
+
|
|
256
|
+
- **Native inputs**: `InputText` and `Textarea` render a real `<input>` / `<textarea>`, so spread `{...formik.getFieldProps(name)}`.
|
|
257
|
+
- **Composite inputs**: `Select`, `Checkbox`, `RadioButtonGroup` and `CheckboxGroup` report through `onValueChange` / `onCheckedChange` with an event object, so push the value with `setFieldValue(name, event.value)`.
|
|
258
|
+
|
|
259
|
+
Bind `invalid` on any component to get error styling.
|
|
260
|
+
|
|
261
|
+
### InputText
|
|
262
|
+
|
|
263
|
+
```tsx title="InputText"
|
|
264
|
+
<div className="flex flex-col gap-2">
|
|
265
|
+
<Label htmlFor="projectName">Project name</Label>
|
|
266
|
+
<InputText id="projectName" {...formik.getFieldProps('projectName')} invalid={formik.touched.projectName && !!formik.errors.projectName} fluid />
|
|
267
|
+
</div>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Select
|
|
271
|
+
|
|
272
|
+
```tsx title="Select"
|
|
273
|
+
<Select.Root
|
|
274
|
+
value={formik.values.framework}
|
|
275
|
+
onValueChange={(e) => formik.setFieldValue('framework', e.value)}
|
|
276
|
+
onBlur={() => formik.setFieldTouched('framework', true)}
|
|
277
|
+
options={FRAMEWORKS}
|
|
278
|
+
optionLabel="label"
|
|
279
|
+
optionValue="value"
|
|
280
|
+
invalid={formik.touched.framework && !!formik.errors.framework}
|
|
281
|
+
>
|
|
282
|
+
<Select.Trigger type="button">
|
|
283
|
+
<Select.Value placeholder="Select a framework" />
|
|
284
|
+
<Select.Indicator>
|
|
285
|
+
<ChevronDown />
|
|
286
|
+
</Select.Indicator>
|
|
287
|
+
</Select.Trigger>
|
|
288
|
+
<Select.Portal>
|
|
289
|
+
<Select.Positioner>
|
|
290
|
+
<Select.Popup>
|
|
291
|
+
<Select.List />
|
|
292
|
+
</Select.Popup>
|
|
293
|
+
</Select.Positioner>
|
|
294
|
+
</Select.Portal>
|
|
295
|
+
</Select.Root>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Checkbox
|
|
299
|
+
|
|
300
|
+
A single checkbox is a boolean, so bind `checked` and forward `event.checked`:
|
|
301
|
+
|
|
302
|
+
```tsx title="Checkbox"
|
|
303
|
+
<Checkbox.Root checked={formik.values.agree} onCheckedChange={(e) => formik.setFieldValue('agree', e.checked)}>
|
|
304
|
+
<Checkbox.Box>
|
|
305
|
+
<Checkbox.Indicator match="checked">
|
|
306
|
+
<Check />
|
|
307
|
+
</Checkbox.Indicator>
|
|
308
|
+
</Checkbox.Box>
|
|
309
|
+
</Checkbox.Root>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Groups
|
|
313
|
+
|
|
314
|
+
`RadioButtonGroup`, `CheckboxGroup` and `ToggleButtonGroup` own a single value for the whole group, so bind the group value and push the change once:
|
|
315
|
+
|
|
316
|
+
```tsx title="CheckboxGroup"
|
|
317
|
+
<CheckboxGroup value={formik.values.environments} onValueChange={(e) => formik.setFieldValue('environments', e.value)}>
|
|
318
|
+
{ENVIRONMENTS.map((env) => (
|
|
319
|
+
<div key={env.value} className="flex items-center gap-2">
|
|
320
|
+
<Checkbox.Root inputId={env.value} value={env.value}>
|
|
321
|
+
<Checkbox.Box>
|
|
322
|
+
<Checkbox.Indicator match="checked">
|
|
323
|
+
<Check />
|
|
324
|
+
</Checkbox.Indicator>
|
|
325
|
+
</Checkbox.Box>
|
|
326
|
+
</Checkbox.Root>
|
|
327
|
+
<Label htmlFor={env.value}>{env.label}</Label>
|
|
328
|
+
</div>
|
|
329
|
+
))}
|
|
330
|
+
</CheckboxGroup>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Value types
|
|
334
|
+
|
|
335
|
+
Most inputs hold a string, but some do not. Match the schema and the `initialValues` to the value type:
|
|
336
|
+
|
|
337
|
+
- `InputNumber`, `Slider`, `Rating` → `number` (`z.number()`)
|
|
338
|
+
- `DatePicker` → `Date` (`z.date()`)
|
|
339
|
+
- `Checkbox`, `ToggleSwitch`, `ToggleButton` → `boolean` (`z.boolean()`)
|
|
340
|
+
- `CheckboxGroup`, multiple `Select`, `InputTags` → `array` (`z.array(...)`)
|
|
341
|
+
|
|
342
|
+
<DocMessage>
|
|
343
|
+
Number and date fields start as `null`, not `''`. An empty string breaks their value type.
|
|
344
|
+
</DocMessage>
|
|
345
|
+
|
|
346
|
+
## Validation
|
|
347
|
+
|
|
348
|
+
### When it runs
|
|
349
|
+
|
|
350
|
+
By default Formik validates on every change and blur. Turn either off with `validateOnChange` and `validateOnBlur`:
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
useFormik({ validateOnChange: false, validateOnBlur: true /* ... */ });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
| Option | Default | Validates |
|
|
357
|
+
| ------------------ | ------- | --------------------------- |
|
|
358
|
+
| `validateOnChange` | `true` | on every keystroke / change |
|
|
359
|
+
| `validateOnBlur` | `true` | when a field loses focus |
|
|
360
|
+
| `validateOnMount` | `false` | once when the form mounts |
|
|
361
|
+
|
|
362
|
+
Formik always validates on submit and marks every field touched, so errors surface even for fields the user never visited.
|
|
363
|
+
|
|
364
|
+
### Showing errors
|
|
365
|
+
|
|
366
|
+
Read the message from `formik.errors[name]` and gate it on `formik.touched[name]` so it only appears after the user has interacted. Render it with the `Message` component in the `simple` variant so it sits inline under the field:
|
|
367
|
+
|
|
368
|
+
```tsx title="Error message"
|
|
369
|
+
<div className="flex flex-col gap-2">
|
|
370
|
+
<Label htmlFor="projectName">Project name</Label>
|
|
371
|
+
<InputText id="projectName" {...formik.getFieldProps('projectName')} invalid={formik.touched.projectName && !!formik.errors.projectName} fluid />
|
|
372
|
+
{formik.touched.projectName && formik.errors.projectName && (
|
|
373
|
+
<Message.Root severity="error" variant="simple" size="small">
|
|
374
|
+
<Message.Content>
|
|
375
|
+
<Message.Text>{formik.errors.projectName}</Message.Text>
|
|
376
|
+
</Message.Content>
|
|
377
|
+
</Message.Root>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Array fields
|
|
383
|
+
|
|
384
|
+
For a repeatable set of fields, such as environment variables, describe the item shape as an array in the schema:
|
|
385
|
+
|
|
386
|
+
```tsx title="schema" showLineNumbers
|
|
387
|
+
const schema = z.object({
|
|
388
|
+
envVars: z.array(
|
|
389
|
+
z.object({
|
|
390
|
+
key: z.string().min(1, 'Key is required.'),
|
|
391
|
+
value: z.string().min(1, 'Value is required.')
|
|
392
|
+
})
|
|
393
|
+
)
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Map over `formik.values.envVars` and mutate the array with `setFieldValue`. Each input targets its row with a dotted `name` (`envVars.0.key`):
|
|
398
|
+
|
|
399
|
+
```tsx title="Array fields" showLineNumbers {2,3,10,13}
|
|
400
|
+
<div className="flex flex-col gap-3">
|
|
401
|
+
{formik.values.envVars.map((row, i) => (
|
|
402
|
+
<div key={i} className="flex items-start gap-2">
|
|
403
|
+
<InputText {...formik.getFieldProps(`envVars.${i}.key`)} placeholder="KEY" />
|
|
404
|
+
<InputText {...formik.getFieldProps(`envVars.${i}.value`)} placeholder="value" />
|
|
405
|
+
<Button
|
|
406
|
+
type="button"
|
|
407
|
+
severity="secondary"
|
|
408
|
+
variant="text"
|
|
409
|
+
onClick={() =>
|
|
410
|
+
formik.setFieldValue(
|
|
411
|
+
'envVars',
|
|
412
|
+
formik.values.envVars.filter((_, idx) => idx !== i)
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
>
|
|
416
|
+
<Times />
|
|
417
|
+
</Button>
|
|
418
|
+
</div>
|
|
419
|
+
))}
|
|
420
|
+
<Button type="button" variant="outlined" onClick={() => formik.setFieldValue('envVars', [...formik.values.envVars, { key: '', value: '' }])}>
|
|
421
|
+
Add variable
|
|
422
|
+
</Button>
|
|
423
|
+
</div>
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
<DocMessage>
|
|
427
|
+
Formik also ships a `<FieldArray>` component with `push` / `remove` helpers. It needs the Formik context, so wrap the tree in `<FormikProvider value={formik}>` to use it.
|
|
428
|
+
</DocMessage>
|
|
429
|
+
|
|
430
|
+
## Nested objects
|
|
431
|
+
|
|
432
|
+
Group related fields under an object in the schema and target them with a dotted `name`:
|
|
433
|
+
|
|
434
|
+
```tsx title="schema"
|
|
435
|
+
const schema = z.object({
|
|
436
|
+
build: z.object({
|
|
437
|
+
command: z.string().min(1, 'Build command is required.'),
|
|
438
|
+
install: z.string().min(1, 'Install command is required.')
|
|
439
|
+
})
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
```tsx title="Nested fields"
|
|
444
|
+
<InputText {...formik.getFieldProps('build.command')} placeholder="npm run build" />
|
|
445
|
+
<InputText {...formik.getFieldProps('build.install')} placeholder="npm install" />
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Initial values follow the same shape: `initialValues: { build: { command: '', install: '' } }`.
|