React Forum
Blog & Guide Responses
Jan 29, 2023 · 04:00 PM

[Guide] Build a Survey App (Submission on Blur as Default) - implementation notes

Started by Morgan Patel
guidesurveyformssubmission on blurasync validationsubmit gatesderived completionroute posture

The Survey App guide takes "submission on blur" seriously: every field submits on blur for tight feedback loops, async validation is always in play, and completion is a derived contract rather than a vibe. I'm interested in how people implemented this without making the UI feel like it has surprises or phantom submissions.

Read the guide

How did you design submit gates so blur-submission doesn't spam the server or confuse users? Did you store derived completion per question/section, and what evidence did you render for it? How did you handle async validation results arriving late (version lanes, severity, retry posture)? What was your strategy for drafts versus committed answers (multi-truth, route documents)?

Comments (24)

Back to latest
Marisol Vega
Feb 01, 2023 · 08:20 PM

Blur submission is only scary when the app hides what it's doing.

We render a tiny per-field lane marker (saving/ok/error) so blur feels intentional.

Dmitri Kline
Feb 06, 2023 · 06:06 AM

Submit gates were the whole game for us. We logged them like contract decisions:

txt
[tips] submitGate field=q12 allowed=false reason=unchanged
[tips] submitGate field=q12 allowed=true reason=blur
[tips] submit lane=pending v=19 reason=submit:blur

And we rendered data-submit-lane + data-submit-v on the question shell so screenshots explain it.

Keira Santos
Feb 12, 2023 · 12:12 PM

Counterpoint: submitting on blur can be hostile on mobile where blur happens accidentally.

We added a posture switch for mobile: blur submits only after an explicit "done" action.

Morgan Patel
Feb 15, 2023 · 04:44 AM

Totally reasonable. The key is that the posture is explicit and logged, not that blur is morally correct.

We also render the posture (data-submit-posture=blur|confirm) so it's debuggable.

Grace McCoy
Feb 25, 2023 · 03:03 AM

Derived completion per section made navigation sane.

We store completionRatio and missingRequiredCount so the progress indicator is stable across routes.

Jasper Nguyen
Mar 05, 2023 · 05:05 AM

We kept drafts local and committed answers only on blur.

If you commit on keystroke, you end up with partial junk answers that break analytics and UI.

Nina Kapoor
Mar 16, 2023 · 10:22 PM

Async validation without cancellation forced us to version the answer. Late results are fine if they are tagged to the version they checked:

txt
[tips] validate field=q12 v=19 lane=ok
[tips] validate field=q12 v=18 lane=ignored reason=staleVersion
Camille Duarte
Mar 29, 2023 · 09:09 AM

We used useFlowEffect to orchestrate blur submit + next focus, and returned doc strings for the log.

It made the flow feel intentional rather than magical.

Owen Price
Apr 15, 2023 · 03:15 PM

Counterpoint: users sometimes *want* to answer a question partially and come back.

We allowed partial commits but flagged them as answerState=draft in stored derived state.

Amina Farouk
Apr 18, 2023 · 04:04 AM

That's a good compromise. "Submit" doesn't have to mean "final" as long as the contract says what it is.

We used answerLane=committed:draft|committed:final as evidence.

Benji Rios
May 03, 2023 · 03:33 AM

Submit gates also prevented double-submits when blur + navigation happen together.

If you don't gate it, you end up with the classic "two answers" bug.

Lina Ortega
May 19, 2023 · 07:19 PM

We rendered evidence per question: data-qid, data-submit-lane, data-validated-v, data-required=1.

Support loved it because they could diagnose from one screenshot.

Theo Bennett
Jun 08, 2023 · 08:08 AM

A lot of the confusion goes away if blur submit is paired with immediate acknowledgment.

We show a tiny "saved" chip with the last submit reason and timestamp.

Sofia Valdez
Jun 29, 2023 · 06:06 AM

We stored derived completion like a reducer output, not like UI sugar. It means completion is correct-by-construction and can be logged:

txt
[tips] derive completion section=demographics ratio=0.66 missingRequired=1
Priya Liao
Jul 11, 2023 · 11:11 AM

Counterpoint: blur submit makes offline mode harder.

We added a lane queued and treated it as success-ish with visible evidence.

Morgan Patel
Jul 14, 2023 · 04:04 AM

Queued lane is a great idea. It's honest and it matches the "lanes" posture.

If you hide queuing, users think the app lost their answer.

Tomas Rivera
Jul 30, 2023 · 10:22 PM

Docs tie-in: blur as default is controversial, but it fits the forms + state posture here: make boundaries explicit.

Related: Forms and Validation (Best Practices) and Core Concepts (Best Practices).

Evan Brooks
Aug 18, 2023 · 06:18 PM

We also had to decide what "blur" means for composite widgets (date pickers, sliders).

We treat the composite as one boundary and submit only when the widget closes.

Harper Iqbal
Sep 02, 2023 · 02:02 AM

One trick: keep the submit payload tiny and do derived summaries server-side too.

If the payload is huge, blur submit becomes expensive and scary.

Ari Novak
Sep 18, 2023 · 12:12 PM

We used a "cooldown gate" so blur submit can't fire twice within 250ms. We log the blocked submits so you can debug it:

txt
[tips] submitGate field=q12 allowed=false reason=cooldown remainingMs=132
Mina Calder
Oct 05, 2023 · 05:05 AM

I was worried about phantom submissions, but the guide's insistence on evidence helped.

If the UI shows why it submitted, it stops feeling haunted.

Noah Vance
Oct 29, 2023 · 09:09 AM

Counterpoint: blur submit can fight accessibility if focus moves unexpectedly.

We didn't auto-advance focus unless the user opted into "fast mode" posture.

Lena Fischer
Nov 11, 2023 · 11:11 AM

The best part of the approach is that it makes surveys feel responsive: you get feedback as you go.

But you must make the lane visible or the feedback feels random.

Imani Brooks
Nov 26, 2023 · 04:44 AM

We stored derived "completion story" as a string (for debugging) in addition to ratios. It printed like:

txt
[tips] completionStory="missing: q12(required), q14(required)"
Salma Qureshi
Dec 12, 2023 · 12:12 PM

If you do async validation, don't hide retries.

We render a retry affordance and log when users press it; otherwise you can't debug trust issues.

Rowan Pierce
Dec 23, 2023 · 11:23 PM

We also made the progress indicator route-driven so it replays correctly with back/forward.

Otherwise progress feels like a separate app state.

Talia Moss
Dec 31, 2023 · 03:03 AM

This pattern sounds extreme, but it's consistent with the overall philosophy here: do work on boundaries, then render evidence.

Blur is just one boundary; the important part is the contract.