@primereact/mcp 11.0.0 → 11.2.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/accordion/api.json +9 -9
- package/data/llms/headless/components/animateonscroll/api.json +2 -2
- package/data/llms/headless/components/autocomplete/api.json +33 -33
- package/data/llms/headless/components/avatar/api.json +1 -1
- package/data/llms/headless/components/carousel/api.json +15 -15
- package/data/llms/headless/components/checkbox/api.json +18 -18
- package/data/llms/headless/components/collapsible/api.json +14 -14
- package/data/llms/headless/components/compare/api.json +3 -3
- package/data/llms/headless/components/contextmenu/api.json +2 -2
- package/data/llms/headless/components/datatable/api.json +19 -19
- package/data/llms/headless/components/dataview/api.json +3 -3
- package/data/llms/headless/components/datepicker/api.json +43 -43
- package/data/llms/headless/components/dialog/api.json +19 -19
- package/data/llms/headless/components/divider/api.json +3 -3
- package/data/llms/headless/components/drawer/api.json +13 -13
- package/data/llms/headless/components/fieldset/api.json +9 -9
- package/data/llms/headless/components/fileupload/api.json +5 -5
- package/data/llms/headless/components/focustrap/api.json +4 -4
- package/data/llms/headless/components/gallery/api.json +23 -23
- package/data/llms/headless/components/inplace/api.json +3 -3
- package/data/llms/headless/components/inputcolor/api.json +6 -6
- package/data/llms/headless/components/inputcolor.md +0 -1
- package/data/llms/headless/components/inputnumber/api.json +8 -8
- package/data/llms/headless/components/inputotp/api.json +7 -7
- package/data/llms/headless/components/inputpassword/api.json +4 -4
- package/data/llms/headless/components/inputpassword.md +3 -3
- package/data/llms/headless/components/inputtags/api.json +8 -8
- package/data/llms/headless/components/inputtext/api.json +1 -1
- package/data/llms/headless/components/knob/api.json +18 -18
- package/data/llms/headless/components/listbox/api.json +27 -27
- package/data/llms/headless/components/menu/api.json +10 -10
- package/data/llms/headless/components/message/api.json +1 -1
- package/data/llms/headless/components/metergroup/api.json +3 -3
- package/data/llms/headless/components/motion/api.json +1 -1
- package/data/llms/headless/components/navigationmenu/api.json +5 -5
- package/data/llms/headless/components/orderlist/api.json +5 -5
- package/data/llms/headless/components/organizationchart/api.json +6 -6
- package/data/llms/headless/components/paginator/api.json +5 -5
- package/data/llms/headless/components/panel/api.json +9 -9
- package/data/llms/headless/components/picklist/api.json +5 -5
- package/data/llms/headless/components/popover/api.json +16 -16
- package/data/llms/headless/components/positioner/api.json +7 -7
- package/data/llms/headless/components/progressbar/api.json +3 -3
- package/data/llms/headless/components/progressspinner/api.json +3 -3
- package/data/llms/headless/components/radiobutton/api.json +16 -16
- package/data/llms/headless/components/rating/api.json +5 -5
- package/data/llms/headless/components/scrollarea/api.json +6 -6
- package/data/llms/headless/components/select/api.json +34 -34
- package/data/llms/headless/components/sidebar/api.json +1116 -141
- package/data/llms/headless/components/sidebar.md +4 -2
- package/data/llms/headless/components/slider/api.json +3 -3
- package/data/llms/headless/components/speeddial/api.json +4 -4
- package/data/llms/headless/components/splitter/api.json +5 -5
- package/data/llms/headless/components/stepper/api.json +13 -13
- package/data/llms/headless/components/styleclass/api.json +1 -1
- package/data/llms/headless/components/tabs/api.json +17 -17
- package/data/llms/headless/components/terminal/api.json +1 -1
- package/data/llms/headless/components/toast/api.json +4 -4
- package/data/llms/headless/components/togglebutton/api.json +27 -3
- package/data/llms/headless/components/toggleswitch/api.json +3 -3
- package/data/llms/headless/components/tooltip/api.json +17 -17
- package/data/llms/headless/components/tree/api.json +20 -20
- package/data/llms/headless/components/tree.md +1 -0
- package/data/llms/headless/components/treetable/api.json +19 -19
- package/data/llms/headless/guides/misc/internationalization.md +282 -0
- package/data/llms/hooks/use-filter.md +2 -2
- package/data/llms/hooks/use-tree-filter.md +3 -1
- package/data/llms/llms-full.txt +10444 -1743
- package/data/llms/llms.txt +15 -0
- package/data/llms/primitive/components/accordion/api.json +39 -39
- package/data/llms/primitive/components/animateonscroll/api.json +5 -5
- package/data/llms/primitive/components/autocomplete/api.json +105 -105
- package/data/llms/primitive/components/avatar/api.json +14 -14
- package/data/llms/primitive/components/badge/api.json +6 -6
- package/data/llms/primitive/components/breadcrumb/api.json +27 -27
- package/data/llms/primitive/components/button/api.json +8 -8
- package/data/llms/primitive/components/buttongroup/api.json +3 -3
- package/data/llms/primitive/components/card/api.json +24 -24
- package/data/llms/primitive/components/carousel/api.json +28 -28
- package/data/llms/primitive/components/checkbox/api.json +14 -14
- package/data/llms/primitive/components/checkboxgroup/api.json +5 -5
- package/data/llms/primitive/components/chip/api.json +19 -19
- package/data/llms/primitive/components/collapsible/api.json +18 -18
- package/data/llms/primitive/components/compare/api.json +18 -18
- package/data/llms/primitive/components/contextmenu/api.json +150 -1141
- package/data/llms/primitive/components/datatable/api.json +215 -164
- package/data/llms/primitive/components/dataview/api.json +17 -17
- package/data/llms/primitive/components/datepicker/api.json +230 -230
- package/data/llms/primitive/components/dialog/api.json +63 -63
- package/data/llms/primitive/components/divider/api.json +3 -3
- package/data/llms/primitive/components/drawer/api.json +47 -47
- package/data/llms/primitive/components/fieldset/api.json +31 -31
- package/data/llms/primitive/components/fileupload/api.json +53 -53
- package/data/llms/primitive/components/floatlabel/api.json +3 -3
- package/data/llms/primitive/components/focustrap/api.json +7 -7
- package/data/llms/primitive/components/gallery/api.json +91 -91
- package/data/llms/primitive/components/iconfield/api.json +7 -7
- package/data/llms/primitive/components/iftalabel/api.json +3 -3
- package/data/llms/primitive/components/inplace/api.json +18 -18
- package/data/llms/primitive/components/inputcolor/api.json +51 -51
- package/data/llms/primitive/components/inputnumber/api.json +10 -10
- package/data/llms/primitive/components/inputotp/api.json +12 -12
- package/data/llms/primitive/components/inputpassword/api.json +7 -7
- package/data/llms/primitive/components/inputtags/api.json +6 -6
- package/data/llms/primitive/components/inputtext/api.json +4 -4
- package/data/llms/primitive/components/knob/api.json +19 -19
- package/data/llms/primitive/components/label/api.json +3 -3
- package/data/llms/primitive/components/listbox/api.json +57 -57
- package/data/llms/primitive/components/menu/api.json +87 -279
- package/data/llms/primitive/components/message/api.json +22 -22
- package/data/llms/primitive/components/metergroup/api.json +27 -27
- package/data/llms/primitive/components/navigationmenu/api.json +8 -8
- package/data/llms/primitive/components/paginator/api.json +43 -43
- package/data/llms/primitive/components/panel/api.json +34 -34
- package/data/llms/primitive/components/popover/api.json +67 -67
- package/data/llms/primitive/components/portal/api.json +4 -4
- package/data/llms/primitive/components/progressbar/api.json +22 -22
- package/data/llms/primitive/components/progressspinner/api.json +18 -18
- package/data/llms/primitive/components/radiobutton/api.json +16 -16
- package/data/llms/primitive/components/rating/api.json +20 -20
- package/data/llms/primitive/components/scrollarea/api.json +23 -23
- package/data/llms/primitive/components/select/api.json +95 -95
- package/data/llms/primitive/components/sidebar/api.json +215 -121
- package/data/llms/primitive/components/sidebar.md +2 -1
- package/data/llms/primitive/components/skeleton/api.json +3 -3
- package/data/llms/primitive/components/slider/api.json +22 -22
- package/data/llms/primitive/components/speeddial/api.json +22 -22
- package/data/llms/primitive/components/splitter/api.json +18 -18
- package/data/llms/primitive/components/stepper/api.json +64 -64
- package/data/llms/primitive/components/tabs/api.json +51 -51
- package/data/llms/primitive/components/tag/api.json +3 -3
- package/data/llms/primitive/components/terminal/api.json +44 -44
- package/data/llms/primitive/components/textarea/api.json +4 -4
- package/data/llms/primitive/components/timeline/api.json +27 -27
- package/data/llms/primitive/components/toast/api.json +48 -48
- package/data/llms/primitive/components/togglebutton/api.json +10 -10
- package/data/llms/primitive/components/togglebuttongroup/api.json +5 -5
- package/data/llms/primitive/components/toggleswitch/api.json +12 -12
- package/data/llms/primitive/components/toolbar/api.json +15 -15
- package/data/llms/primitive/components/tooltip/api.json +52 -52
- package/data/llms/primitive/components/tree/api.json +73 -73
- package/data/llms/primitive/components/visuallyhidden/api.json +3 -3
- 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/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 +182 -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/button/api.json +8 -8
- package/data/llms/styled/components/carousel.md +65 -0
- package/data/llms/styled/components/datatable.md +101 -88
- package/data/llms/styled/components/floatlabel/api.json +3 -3
- package/data/llms/styled/components/fluid/api.json +3 -3
- package/data/llms/styled/components/iconfield/api.json +7 -7
- package/data/llms/styled/components/iftalabel/api.json +3 -3
- package/data/llms/styled/components/inputcolor.md +3 -0
- package/data/llms/styled/components/inputgroup/api.json +7 -7
- package/data/llms/styled/components/label/api.json +3 -3
- package/data/llms/styled/components/menu.md +39 -41
- package/data/llms/styled/components/organizationchart/api.json +33 -33
- package/data/llms/styled/components/rating/api.json +11 -11
- package/data/llms/styled/components/select.md +6 -0
- package/data/llms/styled/components/sidebar.md +12 -8
- package/data/llms/styled/components/tree.md +5 -0
- package/data/llms/styled/components/treetable.md +171 -62
- package/data/llms/styled/guides/configuration.md +223 -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/theming/tailwind.md +25 -0
- package/data/llms/tailwind/components/button/api.json +8 -8
- package/data/llms/tailwind/components/datatable.md +33 -42
- package/data/llms/tailwind/components/inputgroup.md +0 -1
- package/data/llms/tailwind/components/menu.md +5 -5
- package/data/llms/tailwind/components/select.md +3 -0
- package/data/llms/tailwind/components/sidebar.md +16 -11
- package/data/llms/tailwind/components/tooltip.md +4 -13
- package/data/llms/tailwind/guides/misc/internationalization.md +287 -0
- package/data/manifest.json +1089 -779
- package/data/mcp-data.json +263 -32
- package/dist/index.d.ts +7 -2
- package/dist/index.js +1 -1
- package/package.json +9 -8
- package/data/llms/styled/guides/installation/configuration.md +0 -135
- package/data/llms/tailwind/guides/theming/guide.md +0 -179
|
@@ -84,6 +84,44 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
|
|
84
84
|
);
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
The same prop works on a single component, which takes precedence over the provider. This suits an application that is themed overall but has one screen styled differently.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<Select.Root unstyled>…</Select.Root>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
There is also a third option that does not involve the flag at all. Importing from `primereact` gives the unstyled primitives directly, so the theming layer never enters the bundle:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { Select } from '@primereact/ui/select'; // themed, opt out with `unstyled`
|
|
97
|
+
import { Select } from 'primereact/select'; // unstyled by construction
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Preflight
|
|
101
|
+
|
|
102
|
+
Preflight resets the browser defaults on the native elements PrimeReact renders, such as `button`, `input`, `ul`, `table`, `img` and the heading tags. It removes the margins, borders, font inheritance and list styling browsers apply by default, so a component's own styles are the only ones in effect. It is on by default.
|
|
103
|
+
|
|
104
|
+
The reset only applies to elements PrimeReact renders itself, scoped through the `data-scope` attribute each component sets. A `<button>` or `<ul>` outside a PrimeReact component keeps its normal browser defaults.
|
|
105
|
+
|
|
106
|
+
Turn it off with the `preflight` property on the provider:
|
|
107
|
+
|
|
108
|
+
```tsx showLineNumbers {6}
|
|
109
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
113
|
+
<React.StrictMode>
|
|
114
|
+
<PrimeReactProvider preflight={false}>
|
|
115
|
+
<App />
|
|
116
|
+
</PrimeReactProvider>
|
|
117
|
+
</React.StrictMode>
|
|
118
|
+
);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Turn it off in a project that already resets these elements, for example with Tailwind's own preflight, normalize.css or Bootstrap's reboot.
|
|
122
|
+
|
|
123
|
+
An application that loads Tailwind through a cascade layer needs the `primeui` layer declared before the `tailwindcss` import. The reset lives in `primeui.base`, a layer nested inside `primeui`, so declaring `primeui` alone is enough to position both the reset and the theme; a separate `primeui-base` declaration does not reach it and the reset ends up overriding the theme instead. This is also the layer name `theme.options.cssLayer` wraps component styles in when enabled (`primeui` by default, or the name passed to it), so an application already using that option has already declared the layer it needs. See the [Tailwind guide](/docs/styled/guides/theming/tailwind) for the declaration and why the order matters.
|
|
124
|
+
|
|
87
125
|
## PassThrough
|
|
88
126
|
|
|
89
127
|
Defines the shared pass through properties per component type. Visit the [Pass Through Props](/docs/passthrough) documentation for more information.
|
|
@@ -130,6 +168,53 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
|
|
130
168
|
);
|
|
131
169
|
```
|
|
132
170
|
|
|
171
|
+
## CSP
|
|
172
|
+
|
|
173
|
+
Components inject their styles as `<style>` elements at runtime. Under a Content Security Policy that restricts `style-src` to a nonce, those elements are blocked unless they carry the same nonce. The `csp.nonce` property applies it to every stylesheet PrimeReact injects.
|
|
174
|
+
|
|
175
|
+
```tsx showLineNumbers {4}
|
|
176
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
177
|
+
|
|
178
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
179
|
+
<PrimeReactProvider csp={{ nonce: 'YOUR_NONCE_VALUE' }}>
|
|
180
|
+
<App />
|
|
181
|
+
</PrimeReactProvider>
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The nonce has to match the one sent in the response header, and it is applied when a stylesheet is first created.
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
Content-Security-Policy: style-src 'nonce-YOUR_NONCE_VALUE';
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Locale
|
|
192
|
+
|
|
193
|
+
Sets the language used for built-in component messages such as filter operators, month names and screen reader labels. English is registered by default, so this is only needed when another language is required. See the [Internationalization and Localization](/docs/styled/guides/misc/internationalization) documentation for registering languages, translating messages and ready-made translations.
|
|
194
|
+
|
|
195
|
+
```tsx showLineNumbers {4}
|
|
196
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
197
|
+
|
|
198
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
199
|
+
<PrimeReactProvider locale="de">
|
|
200
|
+
<App />
|
|
201
|
+
</PrimeReactProvider>
|
|
202
|
+
);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Languages other than English have to be registered before they can be selected. The `locales` property registers them and selects one in a single step.
|
|
206
|
+
|
|
207
|
+
```tsx showLineNumbers {2,5}
|
|
208
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
209
|
+
import { de } from 'primelocale/js/de.js';
|
|
210
|
+
|
|
211
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
212
|
+
<PrimeReactProvider locale="de" locales={{ de }}>
|
|
213
|
+
<App />
|
|
214
|
+
</PrimeReactProvider>
|
|
215
|
+
);
|
|
216
|
+
```
|
|
217
|
+
|
|
133
218
|
## InputVariant
|
|
134
219
|
|
|
135
220
|
Input fields come in two styles, default is `outlined` with borders around the field whereas `filled` alternative adds a background color to the field.
|
|
@@ -149,3 +234,141 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
|
|
149
234
|
</React.StrictMode>
|
|
150
235
|
);
|
|
151
236
|
```
|
|
237
|
+
|
|
238
|
+
## ZIndex
|
|
239
|
+
|
|
240
|
+
Overlays are layered automatically, and these values set the base each layer starts from. Raise them when PrimeReact overlays need to sit above other fixed elements on the page.
|
|
241
|
+
|
|
242
|
+
```tsx showLineNumbers {4-9}
|
|
243
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
244
|
+
|
|
245
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
246
|
+
<PrimeReactProvider
|
|
247
|
+
zIndex={{
|
|
248
|
+
modal: 1100,
|
|
249
|
+
overlay: 1000,
|
|
250
|
+
menu: 1000,
|
|
251
|
+
tooltip: 1100
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
<App />
|
|
255
|
+
</PrimeReactProvider>
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
| Key | Default | Applies to |
|
|
260
|
+
| --------- | ------- | ------------------------------- |
|
|
261
|
+
| `modal` | 1100 | Dialog, Drawer and other modals |
|
|
262
|
+
| `overlay` | 1000 | Select, DatePicker, Popover |
|
|
263
|
+
| `menu` | 1000 | Menu, ContextMenu |
|
|
264
|
+
| `tooltip` | 1100 | Tooltip |
|
|
265
|
+
|
|
266
|
+
## Defaults
|
|
267
|
+
|
|
268
|
+
Sets default props per component, applied everywhere that component is used. Anything passed at the call site still wins.
|
|
269
|
+
|
|
270
|
+
```tsx showLineNumbers {5-9}
|
|
271
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
272
|
+
|
|
273
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
274
|
+
<PrimeReactProvider
|
|
275
|
+
defaults={{
|
|
276
|
+
Button: { props: { size: 'small', severity: 'secondary' } },
|
|
277
|
+
InputText: { props: { variant: 'filled' } },
|
|
278
|
+
Dialog: { props: { modal: true, closeOnEscape: false } }
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
<App />
|
|
282
|
+
</PrimeReactProvider>
|
|
283
|
+
);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
A call site still overrides what it sets:
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
// Renders large, not small.
|
|
290
|
+
<Button size="large">Save</Button>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
This is the place for house style that would otherwise be repeated on every instance, or wrapped in a local component just to change a default.
|
|
294
|
+
|
|
295
|
+
### Keys
|
|
296
|
+
|
|
297
|
+
The key is the component's registered name. For compound components that name includes the part, and four spellings are accepted so the config reads the way you want it to:
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
<PrimeReactProvider
|
|
301
|
+
defaults={{
|
|
302
|
+
'Checkbox.Root': { props: { size: 'small' } }, // registered name
|
|
303
|
+
CheckboxRoot: { props: { size: 'small' } }, // without the dot
|
|
304
|
+
Checkbox: { Root: { props: { size: 'small' } } }, // nested
|
|
305
|
+
button: { props: { size: 'small' } } // lowercase
|
|
306
|
+
}}
|
|
307
|
+
>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Event handlers
|
|
311
|
+
|
|
312
|
+
Handlers work here too, and they run alongside the one at the call site rather than replacing it. This suits cross-cutting behaviour such as analytics:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
<PrimeReactProvider defaults={{ Button: { props: { onClick: track } } }}>
|
|
316
|
+
<Button onClick={save}>Save</Button>
|
|
317
|
+
</PrimeReactProvider>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Clicking runs `track` and then `save`. The same chaining applies to any function prop.
|
|
321
|
+
|
|
322
|
+
### Dynamic defaults
|
|
323
|
+
|
|
324
|
+
An entry can be a function that returns the props to apply. It receives the base instance, which carries `name`, `id` and the props passed at the call site as `inProps`:
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
<PrimeReactProvider
|
|
328
|
+
defaults={{
|
|
329
|
+
Button: (instance) => ({
|
|
330
|
+
props: { size: instance.inProps?.severity === 'danger' ? 'large' : 'small' }
|
|
331
|
+
})
|
|
332
|
+
}}
|
|
333
|
+
>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## FilterMatchModeOptions
|
|
337
|
+
|
|
338
|
+
Defines which match modes appear in filter menus, grouped by data type. Components such as DataTable read this when building their filter dropdowns.
|
|
339
|
+
|
|
340
|
+
```tsx showLineNumbers {2,6-9}
|
|
341
|
+
import { PrimeReactProvider } from '@primereact/core';
|
|
342
|
+
import { FilterMatchMode } from '@primereact/ui/datatable';
|
|
343
|
+
|
|
344
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
345
|
+
<PrimeReactProvider
|
|
346
|
+
filterMatchModeOptions={{
|
|
347
|
+
text: [FilterMatchMode.CONTAINS, FilterMatchMode.EQUALS],
|
|
348
|
+
numeric: [FilterMatchMode.EQUALS, FilterMatchMode.LESS_THAN],
|
|
349
|
+
date: [FilterMatchMode.DATE_IS, FilterMatchMode.DATE_BEFORE]
|
|
350
|
+
}}
|
|
351
|
+
>
|
|
352
|
+
<App />
|
|
353
|
+
</PrimeReactProvider>
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Leaving it unset keeps the built-in list, which covers the common match modes for each type.
|
|
358
|
+
|
|
359
|
+
## Stylesheet
|
|
360
|
+
|
|
361
|
+
Collects the styles of rendered components so they can be sent with the server response instead of being injected after hydration. This is what prevents a flash of unstyled content in server-rendered applications.
|
|
362
|
+
|
|
363
|
+
```tsx showLineNumbers {2,5,9}
|
|
364
|
+
'use client';
|
|
365
|
+
import { PrimeReactProvider, PrimeReactStyleSheet } from '@primereact/core';
|
|
366
|
+
|
|
367
|
+
const styledStyleSheet = new PrimeReactStyleSheet();
|
|
368
|
+
|
|
369
|
+
export default function Provider({ children }) {
|
|
370
|
+
return <PrimeReactProvider stylesheet={styledStyleSheet}>{children}</PrimeReactProvider>;
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
The [Next.js installation guide](/docs/styled/guides/installation/nextjs) shows the full setup, including how the collected styles are flushed with `useServerInsertedHTML`.
|
|
@@ -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: '' } }`.
|