willdady / form-conventions
Post-save conventions for Platypus frontend forms — where a save lands the user, and what the submit button is called. Use when adding or changing a form in apps/frontend, its submit button, or what happens after it saves.
Frontend Form Conventions
Where a save lands
One rule covers every form: **a save returns the user to the entity's home,
and only leaves the page when the form has no home to return to.** Which of
the two applies falls out of the entity's shape in the route tree:
| Shape | The form is… | On save | Examples |
|---|---|---|---|
| Collection member | the entity's page, reached from a list | go to list | Agents, Blueprints, Skills, Triggers, MCP, Providers, Webhooks, Workspace Context |
| Entity sub-page | a settings/ child of a page the entity owns | stay | Boards, Dashboards |
| Singleton | one per Workspace/Org/account, so there is no list | stay | Sandbox, Organization, Workspace |
| Dialog or inline | not a page at all | stay | Members, Invitations, Global Context, everything in Chat |
Read the shape off apps/frontend/app. settings/agents/[agentId] renders
the agent edit form — an agent has no detail page, so leaving the form means
returning to settings/agents. A board does have a page, and its form lives
at boards/[boardId]/settings, so saving keeps you there.
Staying means mutate() (or router.refresh()) plus a toast. Neverrouter.push to the page you are already on.
Creating always navigates — to the new entity where it has a page of its
own, otherwise to the list it now belongs to.
Deleting always navigates away from whatever it destroyed, whatever the
shape: to the list for a collection member, to the parent otherwise.
A list is wherever the list actually renders, not a route that looks like one.
Triggers navigate to the Workspace home because that is where the trigger list
lives; there is no triggers/ page.
What the button says
The label names the outcome, so it follows from the destination:
| Case | Label |
|---|---|
| Create, page-level form | Save |
| Edit that navigates to the list | Update |
| Edit saved in place | Save |
| Adding to a surface you stay on | Add |
Update is reserved for the edit-then-leave case — it is the word for a save
you are about to be carried away from. Add covers the in-place additions:Add column, Add card, Add widget.
Let the form heading name the entity and keep the button to the bare verb
(Save, not Create board). Prefer the bare verb over Save changes too. A
loading state keeps the same verb as the button it replaces, with the repo's
ASCII ellipsis: Saving... under Save, Updating... under Update.
Unlike the destination rule, this one is ahead of the code. Most forms today
label on {id ? "Update" : "Save"} alone, so a stay-in-place edit can still
read Update (Sandbox, Organization, Workspace, Dashboard settings); #793
brings the existing buttons into line. Write new buttons to the rule above,
and leave the stragglers to that issue.