react-luna-form 0.0.80 → 0.0.82

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.
@@ -0,0 +1,230 @@
1
+ # List Field (`list`)
2
+
3
+ The `list` field type in Luna Form acts as an iterative array structure, enabling users to add, edit, and remove a repeating set of fields dynamically. It's particularly useful when capturing a collection of identical data objects—like a list of addresses, multiple users, or repeated configurations.
4
+
5
+ ---
6
+
7
+ ## Core Properties
8
+
9
+ - **`name`** _(string, required)_: The unique identifier. The resulting form state binds to an array containing objects referencing the child elements.
10
+ - **`type`** _(string, required)_: Evaluates the type definition exactly to `"list"`.
11
+ - **`label`** _(string, optional)_: The human-readable label (typically rendered as a fieldset legend).
12
+ - **`description`** _(string | object, optional)_: Help text or extra context shown globally just under the legend. Supports **Markdown** (specifically links).
13
+ - **`fields`** _(Array<Field | Column | List>, required)_: The nested structure defining what underlying standard HTML inputs are repeated for each iteration in the array. A `list` among them is a list nested inside this one: each row gets its own, named by its path (`groups.0.checks`).
14
+
15
+ ---
16
+
17
+ ## Advanced Configuration (`advanced` object)
18
+
19
+ The `advanced` property customizes structural interaction rules and layout rendering for the collection container.
20
+
21
+ - **`action`** _(string)_: A custom label applied strictly to the generic "Add item" button rendering at the end of the collection (e.g. `"Add email address"`).
22
+ A custom label has to be translated by the form; the built-in `Add item` default is already translated by the library — see [Localization](#localization).
23
+ - **`length`** _({ min?: number, max?: number })_: Configures restrictions on how many items can be generated. Enforces boundary conditions where minimum instances ensure permanent default items and maximum instances automatically disable insertion toggles. The matching error messages are declared under the list's `validation.length` — see the [Validation reference](../validation/overview.md#list-length).
24
+ Both bounds apply to the rows a list opens with, not only to the ones added by hand: an initial `value` shorter than `min` leaves the remainder standing and empty, and one longer than `max` stops at `max`. A [`value` event](../events/change.md#targeting-a-list) assigning rows is clamped the same way.
25
+ - **`collapsed`** _(boolean)_: When enabled, list items default to a visually collapsed representation upon mounting or adding.
26
+
27
+ ### Collapsed Previews (`preview` object)
28
+
29
+ If list items render into structural wrappers (e.g., when they contain multiple fields and become automatically collapsible), the **`preview`** object handles mapping internal field values outward toward the unexpanded accordion headers directly.
30
+
31
+ - **`label`** _(string | PreviewItem)_: Targets a primary field to replace the list element's fallback title string.
32
+ - **`badge`** _(string | PreviewItem)_: Extracts a selected field mapping dynamically as an isolated badge indicator beside the label.
33
+ - **`tags`** _(PreviewItem[])_: Defines an array pointing to additional fields inside the iteration mapping, injected strictly below the label row simulating tag clouds.
34
+
35
+ **`PreviewItem` Definition:**
36
+ Previews can map seamlessly pointing out the desired child `name` simply as strings format, or strictly defined mapping condition rules optionally:
37
+
38
+ - **`field`** _(string)_: The child layout field name.
39
+ - **`label`** _(string)_: Provides contextual prefix naming before mapping the preview value statically.
40
+ - **`when`** _(string | string[] | Condition)_: Standard complex logical operators dictating runtime evaluation rules. When an **array** is provided, it performs an **OR match** against the field's current value.
41
+
42
+ ---
43
+
44
+ ## Filling a list from another field
45
+
46
+ A `list` can be the target of a `value` change event. Aim one at the list's own name and give it an array of objects, and the list becomes exactly those rows — one object per row, each key matching a field name inside it.
47
+
48
+ It assigns rather than adds: the rows you send are the rows that remain, `advanced.length` still bounds the result, and rows that survive are matched by position so they are not remounted.
49
+
50
+ A row's key can name a nested `list` instead of a field, in which case its value is that list's rows and the whole tree is assigned in one event. The full rules and worked examples are in [Targeting a list](../events/change.md#targeting-a-list) and [Rows that hold rows](../events/change.md#rows-that-hold-rows).
51
+
52
+ ---
53
+
54
+ ## Interaction Targeting in Events
55
+
56
+ When interacting internally inside specific items belonging directly to iterators (`list`), Target Actions (like configuring cross dependencies over Change events) inherit relative pathing resolution logically overriding direct top-level values leveraging `"list_name/field_name"` resolution strings.
57
+
58
+ For example, if you want a field inside the list to update another field **in the same iteration/row**, you must prefix the target field's name with the list's name:
59
+
60
+ ```json
61
+ {
62
+ "name": "users",
63
+ "type": "list",
64
+ "fields": [
65
+ {
66
+ "name": "role",
67
+ "type": "select",
68
+ "options": [
69
+ { "value": "admin", "label": "Admin" },
70
+ { "value": "user", "label": "User" }
71
+ ],
72
+ "event": {
73
+ "change": [
74
+ {
75
+ "action": "value",
76
+ "value": {
77
+ "users/access_level": "full_access"
78
+ },
79
+ "when": "admin"
80
+ }
81
+ ]
82
+ }
83
+ },
84
+ {
85
+ "name": "access_level",
86
+ "type": "input/text"
87
+ }
88
+ ]
89
+ }
90
+ ```
91
+
92
+ See [Change Events](../events/change.md#target-resolution-in-lists) structure definitions closely outlining array value mapping techniques cleanly.
93
+
94
+ ---
95
+
96
+ ## Localization
97
+
98
+ Every piece of copy a list renders goes through the `translations` dictionary, keyed by its English source text. The keys split into two groups by who owns the text.
99
+
100
+ **Declared by the form**, so only the form can translate them:
101
+
102
+ - `label` and `description`, rendered as the fieldset legend and its help text.
103
+ - `advanced.action`, the custom add-button label.
104
+ - `advanced.title`, which supplies the caption of each item (`Contact 1`, `Contact 2`, …) and feeds the accessible names of its controls.
105
+
106
+ **Owned by the library**, so `lang` alone is enough — see [built-in translations](#built-in-translations):
107
+
108
+ - `Add item`, the add-button label when no `action` is declared.
109
+ - `Expand {label} {index}`, `Collapse {label} {index}` and `Remove {label} item {index}`, the accessible names of the item controls.
110
+
111
+ ```json
112
+ {
113
+ "lang": "es",
114
+ "translations": {
115
+ "es": {
116
+ "contact_label": "Contacto"
117
+ }
118
+ },
119
+ "sections": [
120
+ {
121
+ "fields": [
122
+ {
123
+ "label": "contact_label",
124
+ "name": "contacts",
125
+ "type": "list",
126
+ "fields": [
127
+ { "name": "name", "label": "Nombre", "type": "input/text" },
128
+ { "name": "email", "label": "Correo", "type": "input/email" }
129
+ ]
130
+ }
131
+ ]
132
+ }
133
+ ]
134
+ }
135
+ ```
136
+
137
+ The list renders as `Contacto`, its add button as `Añadir elemento`, and its controls announce `Contraer Contacto 1` and `Eliminar Contacto 1` — the form only had to name its own label.
138
+
139
+ ### Built-in translations
140
+
141
+ The library ships translations for the copy it renders on its own behalf, because a form cannot name those strings in its schema. Today that covers **Spanish**; any other `lang` falls back to the English source text.
142
+
143
+ Beyond the list keys above, the built-in set also covers the `(Optional)` label suffix, the `Yes`/`No` options of [`select/active`](specialized-selectors.md#5-activebinary-selector-selectactive), and the summary the form shows above itself when a submit fails: `There were validation errors submitting the form.`, `Please correct the errors and try again.`, and `An unexpected error occurred submitting the form.` (plus `Unknown error` for a thrown action carrying no message).
144
+
145
+ The built-ins are layered _under_ the form's own block, so declaring a key still overrides them:
146
+
147
+ ```json
148
+ {
149
+ "lang": "es",
150
+ "translations": { "es": { "Add item": "Agregar contacto" } }
151
+ }
152
+ ```
153
+
154
+ Regional tags resolve to their base language, so `es-MX` and `es-419` both get the Spanish defaults. A form's own entries keep matching on the exact tag, unchanged.
155
+
156
+ ### Accessible name templates
157
+
158
+ The accessible names are single keys carrying `{label}` and `{index}` placeholders rather than separate words, so a translation can reorder the parts — or drop one. The Spanish default for `Remove {label} item {index}` is `Eliminar {label} {index}`, without the English `item` filler:
159
+
160
+ ```json
161
+ {
162
+ "translations": {
163
+ "fr": { "Remove {label} item {index}": "Supprimer {label} {index}" }
164
+ }
165
+ }
166
+ ```
167
+
168
+ Left untranslated, the template fills in to the same English text it replaced.
169
+
170
+ ---
171
+
172
+ ## Complete Example Reference
173
+
174
+ ```json
175
+ {
176
+ "label": "Working Experiences",
177
+ "name": "experiences",
178
+ "type": "list",
179
+ "description": "Please detail your previous relevant jobs.",
180
+ "advanced": {
181
+ "action": "Add Experience Record",
182
+ "collapsed": true,
183
+ "length": {
184
+ "min": 1,
185
+ "max": 3
186
+ },
187
+ "preview": {
188
+ "label": "company",
189
+ "badge": "status",
190
+ "tags": [
191
+ "title",
192
+ {
193
+ "field": "internal_review",
194
+ "label": "Checked",
195
+ "when": {
196
+ "operator": "eq",
197
+ "value": true
198
+ }
199
+ }
200
+ ]
201
+ }
202
+ },
203
+ "fields": [
204
+ {
205
+ "name": "company",
206
+ "label": "Company Name",
207
+ "type": "input/text",
208
+ "required": true
209
+ },
210
+ {
211
+ "name": "title",
212
+ "label": "Job Title",
213
+ "type": "input/text"
214
+ },
215
+ {
216
+ "name": "status",
217
+ "type": "select",
218
+ "options": [
219
+ { "value": "active", "label": "Currently Employed" },
220
+ { "value": "past", "label": "Past Job" }
221
+ ]
222
+ },
223
+ {
224
+ "name": "internal_review",
225
+ "type": "checkbox",
226
+ "hidden": true
227
+ }
228
+ ]
229
+ }
230
+ ```
@@ -0,0 +1,194 @@
1
+ # Select Fields (`select`, `radio`, `chips`)
2
+
3
+ Luna Form provides several field types for selecting one or more values from a set of options. These fields share a common way of managing their data source.
4
+
5
+ ## Supported Types
6
+
7
+ - `select`: A standard dropdown menu (`<select>`).
8
+ - `radio`: A group of radio buttons where only one can be selected.
9
+ - `chips`: Interactive buttons (tags) for selecting values. Supports single or multiple selection.
10
+
11
+ > **Tip:** Luna Form also includes **[Specialized Selectors](specialized-selectors.md)** with built-in sources for months, years, days, timezones, and more.
12
+
13
+ ---
14
+
15
+ ## The `source` Property
16
+
17
+ Unlike input fields, selectors require a list of options. This is managed via the `source` property.
18
+
19
+ ### 1. Static Options
20
+
21
+ You can provide an array of objects directly in the schema.
22
+
23
+ ```json
24
+ "source": [
25
+ { "label": "Apple", "value": "apple" },
26
+ { "label": "Banana", "value": "banana" },
27
+ { "label": "Orange", "value": "orange", "description": "High in Vitamin C" }
28
+ ]
29
+ ```
30
+
31
+ Each option consists of:
32
+
33
+ - **`label`** _(string)_: The text displayed to the user.
34
+ - **`value`** _(string | number | boolean)_: The technical value submitted in the form.
35
+ - **`description`** _(string, optional)_: Additional help text for the specific option.
36
+
37
+ ### 2. Remote Data Source
38
+
39
+ You can fetch options from a remote API by providing a `DataSource` configuration.
40
+
41
+ ```json
42
+ "source": {
43
+ "url": "/api/v1/categories",
44
+ "method": "GET"
45
+ }
46
+ ```
47
+
48
+ If your API returns objects with different keys, you can map them using the `advanced.options` property. This is useful when your data doesn't match the default `{ label, value }` structure.
49
+
50
+ ### Example: Mapping a User Object
51
+
52
+ Suppose your source returns an array of user objects with properties like `id`, `name`, `lastname`, and `age`:
53
+ `[{ "id": 101, "name": "John", "lastname": "Doe", "age": 25 }]`
54
+
55
+ You can use the `options` mapping to decide which properties are used for the selector's value, label, and (optional) description:
56
+
57
+ ```json
58
+ {
59
+ "name": "user_selection",
60
+ "type": "select",
61
+ "source": { "url": "/api/users" },
62
+ "advanced": {
63
+ "options": {
64
+ "value": "id",
65
+ "label": "name",
66
+ "description": "age"
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### Property Mapping Reference
73
+
74
+ Inside `advanced.options`, you can map:
75
+
76
+ - **`label`**: The key to use for the display text (e.g., `"name"`).
77
+ - **`value`**: The key to use for the technical value (e.g., `"id"`).
78
+ - **`description`** _(optional)_: The key to use for additional help text (e.g., `"age"`).
79
+
80
+ ---
81
+
82
+ ## Translating Options
83
+
84
+ When the options are declared inline as an array, `label` and `description` are resolved against the form dictionary, exactly like a field's own `label`. Missing keys fall back to the key itself.
85
+
86
+ ```json
87
+ {
88
+ "lang": "es",
89
+ "translations": {
90
+ "es": {
91
+ "fruit_apple": "Manzana",
92
+ "fruit_banana": "Plátano"
93
+ }
94
+ },
95
+ "sections": [
96
+ {
97
+ "fields": [
98
+ {
99
+ "name": "fruit",
100
+ "type": "select",
101
+ "source": [
102
+ { "value": "apple", "label": "fruit_apple" },
103
+ { "value": "banana", "label": "fruit_banana" }
104
+ ]
105
+ }
106
+ ]
107
+ }
108
+ ]
109
+ }
110
+ ```
111
+
112
+ This applies to `select`, `radio` and `chips`, works with `advanced.options` mapping, and reaches the collapsed preview of a list.
113
+
114
+ Three rules bound the behavior:
115
+
116
+ - **`value` is never translated.** It is the technical value submitted with the form, so the payload stays identical in every language.
117
+ - **Only array sources are translated.** A remote `DataSource` returns data, not authored copy, so its labels are rendered as fetched. The same applies when a `source` change event replaces the array with a remote source at runtime.
118
+ - **Only object options are translated.** In a bare string array (`["apple", "banana"]`) the string is both the label and the value, so translating it would change what the form submits.
119
+
120
+ Specialized selectors carry their own built-in labels and are not resolved against the dictionary. See [Specialized Selectors](specialized-selectors.md#localization).
121
+
122
+ ---
123
+
124
+ ---
125
+
126
+ ## Core Properties
127
+
128
+ - **`name`** _(string, required)_: The unique identifier.
129
+ - **`type`** _(string, required)_: One of the supported select types.
130
+ - **`label`** _(string, optional)_: Human-readable label.
131
+ - **`defaultValue`** _(any, optional)_: The initial selected value. Matches the `value` of one of the options.
132
+ For `chips`, it must be an **array** — see below.
133
+ - **`required`** _(boolean, optional)_: If `true`, a value must be selected.
134
+
135
+ ---
136
+
137
+ ## Advanced Configuration (`advanced` object)
138
+
139
+ - **`multiple`** _(boolean, optional)_: (Available for `chips`) Allows selecting more than one option. Defaults to `true`.
140
+ - **`preselected`** _(boolean, optional)_: For specialized selectors like `select/month`, determines if the current value (e.g., current month) should be selected by default.
141
+ - **`horizontal`** _(boolean, optional)_: Renders the options or selector horizontally.
142
+
143
+ ### A chips value is always an array
144
+
145
+ Whatever `multiple` says, a `chips` field holds an array. The value is coerced
146
+ on the way in: anything that is not an array becomes `[]`.
147
+
148
+ The consequence is a scalar `defaultValue`. This looks reasonable and does
149
+ nothing:
150
+
151
+ ```json
152
+ { "name": "method", "type": "chips", "defaultValue": "GET" }
153
+ ```
154
+
155
+ `"GET"` is not an array, so the field mounts with nothing selected. Nothing
156
+ throws and nothing warns. Write it as an array, single selection or not:
157
+
158
+ ```json
159
+ { "name": "method", "type": "chips", "defaultValue": ["GET"] }
160
+ ```
161
+
162
+ Setting `advanced.multiple` to `false` limits what a user can pick. It does not
163
+ change the shape of the value, so it does not make a scalar default work.
164
+
165
+ ---
166
+
167
+ ## Validation
168
+
169
+ - **`required`** _(string)_: Custom error message when no value is selected.
170
+
171
+ ---
172
+
173
+ ## Complete Example
174
+
175
+ ```json
176
+ {
177
+ "name": "notification_preference",
178
+ "type": "radio",
179
+ "label": "How should we contact you?",
180
+ "defaultValue": "email",
181
+ "source": [
182
+ { "label": "Email", "value": "email" },
183
+ { "label": "SMS", "value": "sms" },
184
+ {
185
+ "label": "Push Notification",
186
+ "value": "push",
187
+ "description": "Requires mobile app"
188
+ }
189
+ ],
190
+ "validation": {
191
+ "required": "Please select a notification method."
192
+ }
193
+ }
194
+ ```
@@ -0,0 +1,175 @@
1
+ # Specialized Selectors
2
+
3
+ Specialized selectors in Luna Form come with pre-defined data sources, such as months, years, or timezones. These allow you to quickly implement common form fields without manually defining a `source` array.
4
+
5
+ ## Date & Time Selectors
6
+
7
+ ### 1. Month Selector (`select/month`)
8
+
9
+ Renders a dropdown with the 12 months of the year.
10
+
11
+ ```json
12
+ {
13
+ "name": "expiry_month",
14
+ "type": "select/month",
15
+ "label": "Expiration Month",
16
+ "required": true
17
+ }
18
+ ```
19
+
20
+ ### 2. Year Selector (`select/year`)
21
+
22
+ Renders a dropdown with a range of years.
23
+
24
+ - **Default behavior**: Shows the current year plus the next 5 years.
25
+ - **Customization**: Use `advanced.length.min` and `advanced.length.max` to define the start and end years relative to the current year.
26
+
27
+ ```json
28
+ {
29
+ "name": "expiry_year",
30
+ "type": "select/year",
31
+ "label": "Expiration Year",
32
+ "advanced": {
33
+ "length": {
34
+ "min": 0,
35
+ "max": 10
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 3. Day Selector (`select/day`)
42
+
43
+ Renders a dropdown with days 1 through 31.
44
+
45
+ ```json
46
+ {
47
+ "name": "birth_day",
48
+ "type": "select/day",
49
+ "label": "Day"
50
+ }
51
+ ```
52
+
53
+ ### 4. Timezone Selector (`select/timezone`)
54
+
55
+ Provides a comprehensive list of global timezones.
56
+
57
+ ```json
58
+ {
59
+ "name": "user_timezone",
60
+ "type": "select/timezone",
61
+ "label": "Your Timezone",
62
+ "defaultValue": "America/New_York"
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Utility Selectors
69
+
70
+ ### 5. Active/Binary Selector (`select/active`)
71
+
72
+ A simple binary toggle, typically rendered as "Yes" and "No".
73
+
74
+ ```json
75
+ {
76
+ "name": "is_active",
77
+ "type": "select/active",
78
+ "label": "Account Active?",
79
+ "defaultValue": "true"
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## specialized Chips
86
+
87
+ These types render as buttons (tags) instead of dropdowns.
88
+
89
+ ### 6. Day Chips (`chips/day`)
90
+
91
+ Renders chips for the 7 days of the week.
92
+
93
+ ```json
94
+ {
95
+ "name": "available_days",
96
+ "type": "chips/day",
97
+ "label": "Select Days",
98
+ "advanced": {
99
+ "multiple": true
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### 7. Month Chips (`chips/month`)
105
+
106
+ Renders chips for the 12 months of the year.
107
+
108
+ ```json
109
+ {
110
+ "name": "subscription_months",
111
+ "type": "chips/month",
112
+ "label": "Subscription Months"
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Localization
119
+
120
+ The option labels of these selectors are produced by the framework, not written in the schema, so most of them are resolved through the form's `lang` rather than against the `translations` dictionary the way an inline [array source](select.md#translating-options) is.
121
+
122
+ - `select/month`, `select/day`, `chips/day` and `chips/month` take their names from the form's `lang`, resolved through `Intl`. Without `lang` they fall back to the runtime locale, and a malformed tag (`es_MX`, `español`) falls back too rather than failing.
123
+ - `select/year` renders plain numbers.
124
+ - `select/timezone` renders its region groups and zone names in English.
125
+ - `select/active` is the exception: `Yes` and `No` are authored copy rather than locale data, so they **are** resolved against the dictionary, using their English text as the key — the same way the built-in `(Optional)` suffix is. The library ships the Spanish for both, so `lang: "es"` alone is enough; see [built-in translations](list.md#built-in-translations).
126
+
127
+ ```json
128
+ {
129
+ "lang": "es",
130
+ "sections": [
131
+ {
132
+ "fields": [{ "name": "month", "type": "select/month" }]
133
+ }
134
+ ]
135
+ }
136
+ ```
137
+
138
+ The dropdown lists `enero` through `diciembre`, while the submitted value stays the month number. Setting `lang` also keeps the server and client renders on the same locale; without it each side resolves its own default, which can differ.
139
+
140
+ `select/active` instead reads its two labels from the dictionary, and the library already ships the Spanish for them:
141
+
142
+ ```json
143
+ {
144
+ "lang": "es",
145
+ "sections": [
146
+ {
147
+ "fields": [{ "name": "active", "type": "select/active" }]
148
+ }
149
+ ]
150
+ }
151
+ ```
152
+
153
+ The dropdown lists `Sí` and `No`, while the submitted value stays the boolean. A `lang` with no built-in dictionary renders them in English, and a form that wants different wording declares the keys itself:
154
+
155
+ ```json
156
+ {
157
+ "lang": "es",
158
+ "translations": { "es": { "Yes": "Activo", "No": "Inactivo" } }
159
+ }
160
+ ```
161
+
162
+ To control the wording further — or to localize any of the locale-driven selectors — declare an explicit `source`. It overrides the built-in options and, being an array, its labels go through the dictionary:
163
+
164
+ ```json
165
+ {
166
+ "name": "active",
167
+ "type": "select/active",
168
+ "source": [
169
+ { "value": "true", "label": "active_yes" },
170
+ { "value": "false", "label": "active_no" }
171
+ ]
172
+ }
173
+ ```
174
+
175
+ For `select/timezone` the override must keep the group shape (`{ "label": "...", "items": [...] }`) that the grouped selector expects.
package/docs/index.md ADDED
@@ -0,0 +1,53 @@
1
+ # react-luna-form
2
+
3
+ Reference for the JSON that drives a luna form. These files ship inside the
4
+ published package, so what you are reading always describes the version you have
5
+ installed.
6
+
7
+ Forms are declared as data, not composed as components: a `sections` array of
8
+ field descriptors is the single source for what renders **and** for the Zod
9
+ schema that validates it. Most mistakes come from assuming otherwise.
10
+
11
+ Start at [setup.md](setup.md) if you are wiring the form up for the first
12
+ time: entry points, `defineConfig`, and the difference between the client and
13
+ server `Form`.
14
+
15
+ ## Where to look
16
+
17
+ | You are | Read |
18
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------ |
19
+ | Declaring a text, number, date or password field | [fields/input.md](fields/input.md) |
20
+ | Declaring a `select`, `radio` or `chips` field, or filling one from a source | [fields/select.md](fields/select.md) |
21
+ | Declaring a repeatable group of fields | [fields/list.md](fields/list.md) |
22
+ | Looking for a date range, a color or another ready-made picker | [fields/specialized-selectors.md](fields/specialized-selectors.md) |
23
+ | Implementing a field type this library does not ship | [fields/custom-inputs.md](fields/custom-inputs.md) |
24
+ | Making a field required, or validating against another field | [validation/overview.md](validation/overview.md) |
25
+ | Showing, hiding, clearing or filling a field when another one changes | [events/change.md](events/change.md) |
26
+ | Formatting an interpolated value inside a label or a message | [interpolation/format-filters.md](interpolation/format-filters.md) |
27
+
28
+ ## Three things that surprise people
29
+
30
+ Read the relevant page before writing the field, not after debugging it.
31
+
32
+ - **A field type nothing implements renders nothing at all.** `config.inputs`
33
+ is an exact map, with no prefix resolution: a `type` that was never
34
+ registered resolves to `undefined` and the field is skipped, on the server
35
+ and on the client alike. No error, no placeholder — the field is simply
36
+ absent from the form.
37
+ - **`defineConfig({ validation })` replaces the defaults, it does not merge.**
38
+ Passing `{ change: false }` also turns off `blur`, `submit` and
39
+ `showError` — the form loses its error summary and nothing says why. Pass
40
+ every key you still want.
41
+ - **A `change` event also runs at mount.** A field that arrives with a value or
42
+ a `defaultValue` fires its `change` events once, so anything they reveal,
43
+ clear or fetch happens before the user touches the form. See
44
+ [events/change.md](events/change.md).
45
+
46
+ ## Entry points
47
+
48
+ | Import | Use |
49
+ | ------------------------ | ------------------------------------------------------------------------------------------- |
50
+ | `react-luna-form` | The `Form` component and client hooks |
51
+ | `react-luna-form/config` | `defineConfig`, `defineInput`, `defineCustomInput` and the rest of the registration helpers |
52
+ | `react-luna-form/schema` | Building and inspecting the Zod schema derived from `sections` |
53
+ | `react-luna-form/server` | Server-safe helpers, usable under the `react-server` condition |