You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(ui): state the type: 'form' action contract — in-shell, submit lands on the record (#7245)
`type: 'form'` is the platform's first-class way to open an object's form,
but nothing in the hand-written docs said what it promises the author. The
docs described the route and the four `submitBehavior` kinds, and flatly
labelled `thank-you` "(default)" — which is today's mode-blind renderer
fallback, not the contract.
State the ruled contract where authors read about form actions:
- Action Protocol / Form Actions gains a "What `type: 'form'` promises"
subsection — renders in-shell (the route stays, so deep-linking survives),
internal submit defaults to a redirect to the created record, and an
explicit `submitBehavior` always wins — plus a public-vs-internal table.
- Forms guide §8 replaces the bare "(default)" marker with a mode-aware
defaults table: `thank-you` for the public `/console/f/:slug` path,
redirect-to-created-record for the internal path.
- Forms guide §9 and the two action tables (`ui/actions`, protocol
`Action Types`) restate the in-shell/lands-on-record clause and link the
contract.
- The public-collection guide says the explicit `thank-you` there is copy,
not behavior, and points at the split.
Where the shipped renderer does not match the contract yet, the pages say so
inline rather than documenting the defect: a dated "current renderer status"
note on both pages pointing at the ObjectUI half tracked from the #7245
thread.
Part of #7245.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnGHxdcvU63spYLamVnpkX
Copy file name to clipboardExpand all lines: content/docs/protocol/objectui/actions.mdx
+28-1Lines changed: 28 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,7 +49,7 @@ The `type` field selects how an action is dispatched. The complete enum is **`sc
49
49
| `flow` | Invoke a Screen/automation Flow by name. | Yes |
50
50
| `modal` | Open a modal/page by name. | Yes |
51
51
| `api` | Call an API endpoint (`method` defaults to `POST`). | Yes |
52
-
| `form` | Open a FormView by name, routed to `/console/forms/:name`. | Yes |
52
+
| `form` | Open a FormView by name, routed to `/console/forms/:name` **inside the console shell**; the submit lands on the created record. | Yes |
53
53
54
54
`target`is the canonical binding for every non-`script` type, and since protocol 17 it is the **only** one. The deprecated `execute` alias was removed (#3855): authoring it is rejected with an error naming the replacement, not silently stripped. Run `os migrate meta --from 16` to rewrite existing sources automatically — the removal also ships in the machine-readable change manifest (`spec-changes.json`), which composes across however many majors you are jumping.
55
55
@@ -159,6 +159,33 @@ type: form
159
159
target: customer_quick_edit
160
160
```
161
161
162
+
#### What `type: 'form'` promises
163
+
164
+
`type: 'form'` is the platform's first-class way to open an object's form — the shape that replaced the `type: 'modal'` object fallback — so its contract is stated here rather than left to each renderer to decide (ruled 2026-08-10 on [#7245](https://github.com/objectstack-ai/objectstack/issues/7245)):
165
+
166
+
1. **It renders in-shell.** The internal form route nests **inside** the console shell: sidebar, app navigation and breadcrumb stay. A form action never drops the operator onto an unchromed standalone page with the browser Back button as the only way home. The route survives too — `/console/forms/:name` stays deep-linkable and bookmarkable, which is what a route buys you over a transient dialog.
167
+
2. **A successful submit lands on the created record.** On the internal path the default post-submit behavior is a **redirect to the record that was just created**, not a confirmation panel. The `thank-you` panel is the default for the **public** `/console/f/:slug` path only, where there is no record the anonymous submitter is allowed to see.
168
+
3. **An explicit `submitBehavior` on the FormView always wins**, in either mode. The defaults above are what you get for declaring nothing; they are not a ceiling.
169
+
170
+
| | Public path (`/console/f/:slug`) | Form action / internal path (`/console/forms/:name`) |
171
+
| --- | --- | --- |
172
+
| Audience | anonymous visitors | authed operators — where `type: 'form'` sends them |
173
+
| Chrome | standalone page, no console chrome | nested inside the console shell |
174
+
| Default when `submitBehavior` is omitted | `thank-you` confirmation panel | redirect to the created record |
175
+
176
+
The four `submitBehavior` kinds and their options are documented on the [Forms guide](/docs/ui/forms).
177
+
178
+
<Callout type="warn">
179
+
**Current renderer status (2026-08-10).** The console shipping at the pinned
180
+
`.objectui-sha` does **not** implement this contract yet: it renders
181
+
`/console/forms/:name`as a top-level page outside the shell, and it falls back
182
+
to the `thank-you` panel in both modes. The statement above is the contract —
183
+
the renderer half is tracked in ObjectUI and linked from the
184
+
[#7245 thread](https://github.com/objectstack-ai/objectstack/issues/7245). Until
185
+
it lands, an internal FormView that must land on its new record has to declare
186
+
`submitBehavior`explicitly.
187
+
</Callout>
188
+
162
189
### Modal Actions
163
190
164
191
Open a modal/page by name. `target` is resolved as a **page** first, then as an object (which opens that object's create/edit form); when it names neither, the action falls through to its server-side handler.
Copy file name to clipboardExpand all lines: content/docs/ui/actions.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,7 +21,7 @@ The types you'll actually use:
21
21
|`url`| Navigate / open a link |`target` is the URL (`${ctx.record.id}` interpolation supported) |
22
22
|`modal`| Open a modal page — client-side only, no server dispatch |`target` names the modal page (to collect input *and* run logic, use `script` + `params`) |
23
23
|`api`| Call an HTTP endpoint directly |`target` is the endpoint; `method` / `bodyShape` / `bodyExtra` shape the request |
24
-
|`form`| Open a form view, prefilled with the current record |`target` names the FormView; routed to `/forms/:target?recordId=…`|
24
+
|`form`| Open a form view, prefilled with the current record — in-shell, and the submit lands on the created record ([contract](/docs/protocol/objectui/actions#what-type-form-promises)) |`target` names the FormView; routed to `/forms/:target?recordId=…`|
25
25
26
26
<Callouttype="warn">
27
27
Prefer `confirmText`/`params` over the two action properties that are **not
- Honor `submitBehavior` (thank-you / redirect / continue / next-record) — with **mode-aware defaults** when it is omitted (see [§8](#8-submitbehavior--what-happens-after-submit))
21
21
22
22
A **public form** is the Salesforce *Web-to-Lead* style embeddable form — declare a `FormView` with `sharing.allowAnonymous: true`, give it a `publicLink`, and the framework wires the anonymous REST endpoints automatically.
23
23
@@ -341,14 +341,27 @@ formViews: {
341
341
342
342
|`kind`| Renderer behavior |
343
343
|---|---|
344
-
|`thank-you`*(default)*| Replace the form with a confirmation panel (`title`, `message`). |
344
+
|`thank-you`| Replace the form with a confirmation panel (`title`, `message`). |
345
345
|`redirect`|`window.location.assign(url)` after `delayMs` (defaults to 0). |
346
346
|`continue`| Re-read prefill values and reset state — user can submit another response without reload. |
347
347
|`next-record`| Stub for queue contexts; falls back to thank-you when no queue is wired. |
348
348
349
+
### The default is mode-aware
350
+
351
+
`submitBehavior` is optional, and **what you get for omitting it depends on the mode** — a public collection form and an authed create have different right answers (ruled 2026-08-10 on [#7245](https://github.com/objectstack-ai/objectstack/issues/7245)):
352
+
353
+
| Mode | Default when `submitBehavior` is omitted | Why |
354
+
|---|---|---|
355
+
|**Public** (`/console/f/:slug`) |`{ kind: 'thank-you' }` — the confirmation panel | The anonymous submitter may not read the record back, so a receipt is all there is to show. |
356
+
|**Internal** (`/console/forms/:name`) | Redirect to the **created record**| An operator who just created a record belongs on that record, not on a "submission received" receipt. |
357
+
358
+
An explicit `submitBehavior` overrides the default in either mode, so nothing here removes an option — it only changes what an author gets for declaring nothing.
359
+
360
+
> **Current renderer status (2026-08-10).** The console shipping at the pinned `.objectui-sha` still applies `thank-you` as the default in **both** modes. The table above is the contract; the renderer half is tracked in ObjectUI and linked from the [#7245 thread](https://github.com/objectstack-ai/objectstack/issues/7245). Until it lands, declare `submitBehavior` explicitly on an internal FormView that must land somewhere specific.
361
+
349
362
## 9. `type: 'form'` action — declarative form launchers
350
363
351
-
App actions can declare `type: 'form'` to open a FormView without resorting to free-form URLs. The `target` is the FormView name; the runtime navigates to `/console/forms/:name`.
364
+
App actions can declare `type: 'form'` to open a FormView without resorting to free-form URLs. The `target` is the FormView name; the runtime navigates to `/console/forms/:name`**inside the console shell** — the operator keeps the sidebar, navigation and breadcrumb, and a successful submit lands on the created record per the mode-aware default above. The full contract is stated on the [Action Protocol page](/docs/protocol/objectui/actions#what-type-form-promises).
Copy file name to clipboardExpand all lines: content/docs/ui/public-data-collection.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,6 +34,8 @@ formViews: {
34
34
}
35
35
```
36
36
37
+
The `submitBehavior` above sets the **copy**, not the behavior: `thank-you` is already the default on the public path, because an anonymous submitter may not read the record back. The internal path defaults the other way — a `type: 'form'` action lands the operator on the record it just created — so do not carry a public form's confirmation panel over to an internal one by habit. See [`submitBehavior` is mode-aware](/docs/ui/forms#the-default-is-mode-aware).
38
+
37
39
That `sharing` block wires the anonymous endpoints automatically:
0 commit comments