The Evolution Of The Form
Headless, accessibility-first React components for building intelligent forms.
A survey, an interview, a job application, a signup: underneath, they are all forms. Each one gathers answers to a set of questions decided in advance. On the web that reduces to one element and one idea: collect a person's answer, typed or spoken, then send it somewhere. The rest of this page is what happens when that idea learns to reason about the answer.
The form was built to collect and submit
The <form> element exists so a page can gather input from a person and hand it to a server. When
the form submits, the browser collects the named fields and sends them as a single request. That is
the entire contract.
<form action="/feedback" method="post">
<label for="reason">What made you cancel?</label>
<textarea id="reason" name="reason"></textarea>
<button>Submit</button>
</form>It is a faithful courier. It does not read the answer, and it was never meant to.
Then we added validation to hold a standard
Raw input is messy, so we validate it, usually on the server, before accepting it. Validation enforces a standard for each field.
// on the server
if (!reason || reason.length < 10) {
return { errors: { reason: "Please add a little more detail." } };
}Useful, but look at the ceiling. Validation checks the shape of an answer, never its substance. "Twelve characters long" is not "a useful answer." An evasive reply that clears the length check still gets in. A thoughtful reply worth exploring is accepted and filed without a second thought. The follow-up a good interviewer would ask never happens, because the exchange has only two outcomes: pass, or a list of errors.
What if validation could reason?
Keep the mental model of a form and its fields. Replace the pass-or-fail rule with something that can think.
Now each field is appraised by an agent. It can validate, qualify, and assist: accept a strong answer, ask a sharper question when an answer is thin, offer a quick choice when that is faster, and reshape the field at runtime, field by field. Instead of returning a batch of errors after the fact, it responds in the moment.
That is a Cell: a form field that reasons.
import { DebrieferProvider, Form, Textarea, Button } from "@debriefer/blocks-react";
<DebrieferProvider config={{ formId, sessionToken }}>
<Form>
<Textarea id="reason" label="What made you cancel?" />
<Button>Submit</Button>
</Form>
</DebrieferProvider>The same field you already know how to write. The difference shows the moment someone answers.
One answer becomes a thread
The respondent types two words. From that single field, the agent builds a thread, qualifying the answer one question at a time.
Field
What made you cancel?
That field asked one question and came away with a reason, a cause, and a specific feature. Validation could only have told you the answer was long enough.
Underneath, each turn is a single call. The field submits an answer for its node, and the agent returns the next move: ask again, offer a choice, or finish.
const decision = await submitAnswer("reason", "Too expensive");
// { type: "follow_up", prompt: "Compared to what you were paying, or the value you were getting?" }The thread is that loop, and it stays anchored to its node. A form stops being a list of fields and becomes a set of small, focused conversations, each running field by field.
The agent closes the loop
When the thread has what it needs, the agent does two things a validator never could.
First, it writes a conclusive summary of the answer, the qualified result rather than the raw words:
Summary
Churned on value rather than price. Low feature use, mainly the analytics dashboards.
Second, it can offer that summary back to the respondent to confirm before anything is submitted:
What reaches your backend is both the clean answer and the thread that produced it, ready to store in whatever datastore you already use.
{
field: "reason",
answer: "Churned on value rather than price. Low feature use, mainly the analytics dashboards.",
confirmed: true,
thread: [
{ role: "respondent", text: "Too expensive." },
{ role: "agent", text: "Compared to what you were paying, or the value you were getting?" },
{ role: "respondent", text: "The value, really. I was only using a fraction of it." },
// remaining turns
],
}You store the summary as the answer, and keep the thread as the evidence behind it.
Validation became a conversation, and you wrote an ordinary form to get it.
What you write, and what the agent does
You keep everything a form developer expects to own. The agent takes over the part that used to be a blunt rule.
| You own | The agent owns |
|---|---|
| Which fields exist, and their layout | Whether an answer is sufficient |
| Styling, through your own components | When to ask a follow-up, and what to ask |
| The shape of each input | When to offer a choice or move on |
| How the field changes at runtime | |
| The summary it concludes, and the optional confirm step |
The one rule
The agent is the sole decision maker. A Cell renders what the agent returns and submits what the respondent enters, nothing more. A Cell never appraises an answer, advances on its own, caps follow-ups, or judges whether an answer is enough: that is always the agent's call, not the client's. See the model.