React Forum
Blog & Guide Responses
Jul 12, 2022 · 04:00 PM

[Deep Dive #14] Form Authority - discussion

Started by Morgan Patel
blogformsauthorityFielddom truthevidence

I'm trying to settle a recurring debate on our team: what is the "authority" for a form while the user is actively typing, and when do you promote that authority into the route contract?

Read the post

Do you keep a typing buffer (DOM truth) and commit on blur/navigation, or commit on every keystroke? What evidence do you render so validation and "what got committed" is explainable from a screenshot? How do you keep multi-panel forms deterministic (review panel, summary sidebar, validation list) without duplicating derivations?

Comments (10)

Back to latest
Keira Santos
Jul 12, 2022 · 04:06 PM

When you say authority, do you mean "what shows on screen" or "what gets validated/submitted"?

We keep mixing those and it causes arguments.

Morgan Patel
Jul 12, 2022 · 04:10 PM

Exactly. I mean: which value is the contract at any moment.

What shows on screen can be a local buffer, but what gets validated/serialized needs to be stable and visible.

Keira Santos
Jul 12, 2022 · 04:13 PM

Ok, that helps. So it isn't "controlled vs uncontrolled", it's "buffer vs contract".

Dmitri Kline
Jul 12, 2022 · 04:18 PM

Our default is: typing buffer locally, commit to doc on blur/navigation.

Then every panel reads from the committed doc, and we render evidence that tells you what commit boundary you crossed.

Related: Forms and Validation Best Practices and the "render-visible contract signal" stance in Testing and Debugging.

Salma Qureshi
Jul 12, 2022 · 04:26 PM

A pattern we liked was making commit explicit at the Field boundary, not sprinkled across inputs:

tsx
function Field({ name }: { name: string }) {
  const doc = useRouteDoc('profile');
  const commit = (next: string) => {
    doc.write(name, next);
    doc.write('lastCommit', { field: name, at: Date.now() });
  };
  return <Input defaultValue={doc.read(name)} onBlur={(e) => commit(e.currentTarget.value)} />;
}

Now the review panel never reads from typing buffers, and the evidence tells you which field last committed.

Hana Saito
Jul 12, 2022 · 04:35 PM

Counterpoint: commit-on-blur can be surprising when users expect live validation.

We did commit-on-change for small fields (title, toggles) and blur for big text, but we rendered which posture we were in as evidence so it wasn't guesswork.

Theo Bennett
Jul 12, 2022 · 04:40 PM

Same. The important thing is not the choice, it's making it deterministic.

If "validation is based on buffer" sometimes and "based on doc" other times, you get un-debuggable bugs.

Maya Henson
Jul 12, 2022 · 04:48 PM

The multi-panel point is real. If the sidebar reads from buffer and the review panel reads from committed state, users will think the app is broken.

Amina Farouk
Jul 12, 2022 · 04:56 PM

We fixed a bunch of validation mismatch bugs by storing derived evidence once.

We store missingFields and completionPercent on the doc and both panels read it, instead of each panel deriving "missing" independently.

Jules Kwon
Jul 12, 2022 · 05:10 PM

Evidence we render that helped the most: lastCommit, validationLane, and dirtyFieldsCount.

It turns bug reports into "lastCommit=email validationLane=strict" instead of "it didn't save".

Inez Park
Jul 12, 2022 · 05:22 PM

If you have steppers, step transitions should force a commit boundary.

Otherwise people can navigate away with a buffer and your route contract is lying to back/forward.

Benji Rios
Jul 12, 2022 · 05:36 PM

We also versioned form docs during migrations.

If you change a field meaning, that's a contract change and evidence should say which contract you're on.

Grace McCoy
Jul 12, 2022 · 05:49 PM

My gut check: can support read a screenshot and tell whether the user is complaining about buffer vs committed state? If not, add one evidence key.