Build a case intake form
In this guide, you build a support case intake form and run it in the React and Vite starter. The form has:
- contact fields that appear, and become required, based on the preferred contact method;
- a priority calculated from the answers;
- a response date calculated from the priority; and
- a business impact field that is required only for high-priority cases.
All of this behavior lives in one JSON schema. The application code only registers the form.
Time: about 15 minutes.
Before you start
- Node.js
22.19.0+ - form0-cli, installed with
npm install --global form0-cli
The React + Vite starter includes the required form0 packages. If you are using an existing
application, it needs at least form0-core 0.4.1 and form0-react 0.2.2. Follow
Install and render with React to install or update them before
copying the schema.
Build the form
-
Create a web app with the starter:
CodeChoose Web app, then React + Vite. Then enter the project directory:
Code -
Create
src/forms/case-intake/schema.jsonwith this schema:CodeIn this schema, each field's
keymatches itsdata_nameto keep the conditions easy to read. When you add fields with the CLI, it generates random keys instead. -
Register the form in
src/forms/registry.jsby adding an entry to theformsarray:Code -
Start the app and the form0 development server:
CodeThe CLI installs the dependencies on the first run, validates the schema, watches it for changes, and starts the Vite server.
-
Open
http://localhost:5173/case-intakeand try the form:- Choose Email as the contact method. The Email field appears and is required. Switch to Phone and the Phone field replaces it.
- Choose My team and answer No to Is work blocked?. Priority shows
normal, and Respond by is three days after Received on. - Change the answer to Yes. Priority becomes
high, Respond by moves to the next day, and Describe the business impact appears as a required field.
-
Fill in the required fields and submit the form. Open your browser's developer console to see the structured record that the starter logs.
How it works
Conditional fields
The Email field is hidden by default. Its conditions show it, and make it required, when the
contact method is email:
Code
A condition on a choice field compares the selected choice value, not its label. See
Conditions and operators.
Hiding a field does not clear its value. If someone types an email address and then switches to Phone, the email address is still in the form values. Decide in your application whether hidden values should be kept.
Calculated priority
priority is a CalculatedField. Its calculate expression reads the answers to Who is
affected? and Is work blocked?:
Code
CHOICEVALUE() returns the selected value of a choice field. The first IF() keeps the priority
empty until Who is affected? has an answer.
The expressions on this page are split across lines for readability. In the schema, each one stays
on a single line. If you write a multiline calculation, provide its result with
SETRESULT() or an explicit return statement.
Response date
respond_by adds a number of days to received_on, based on the priority:
Code
DATEADD() returns null when the number of
days is blank, so the date stays empty until there is a priority. "display": { "style": "date" }
tells the renderer to format the result as a date.
A condition driven by a calculation
Describe the business impact uses the same kind of condition as Email, but it reads
priority, which is itself calculated:
Code
This works because the engine runs calculations before it evaluates conditions.
What just happened
Each time you changed an answer, the starter's renderer passed it to the form0 engine. The engine
re-ran priority and then respond_by, in dependency order, and then re-evaluated which fields are
visible and required. The renderer only displayed the result. The same schema produces the same
behavior in any application that uses form0.
To go deeper:
- Evaluation cycle: what one evaluation pass does, and in what order.
- Calculations and dependencies: how the engine orders calculations that depend on each other.
- Submissions and connectors: where the submitted record goes in the starter, and how to send it to a database.