> ## Documentation Index
> Fetch the complete documentation index at: https://docs.serval.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Logic and workflows

> Show fields conditionally, pull live data while the form is open, and hand the answers to a workflow.

A form re-renders every time an answer changes, so it can ask different questions, fetch real data, and validate across fields before anything is submitted.

Two kinds of workflow connect to a form, and the workspace shows both:

| Workspace card           | When it runs                      | What it's for                                     |
| :----------------------- | :-------------------------------- | :------------------------------------------------ |
| **Runs inside the form** | While the form is being filled in | Fetching live data to show or shape the questions |
| **Runs on submit**       | Once, on submit                   | Doing the work the request asked for              |

## Conditional questions

A field's current answer is an ordinary value, so logic is ordinary code. No rule builder, no separate condition language.

```tsx theme={null}
const [forSomeoneElse, ForSomeoneElse] = field.boolean("for_someone_else", {
  label: "Is this for someone else?",
});
const [recipient, Recipient] = field.user("recipient", { label: "Who's this for?" });

return (
  <Form>
    <ForSomeoneElse />
    {forSomeoneElse && <Recipient required />}
  </Form>
);
```

| Goal                                  | How                                                          |
| :------------------------------------ | :----------------------------------------------------------- |
| Show a field only when relevant       | Render it inside a condition                                 |
| Require a field only sometimes        | `<Field required={isContractor} />`                          |
| Narrow one field's options by another | Filter the entity or user picker on the other field's answer |
| Warn about a combination              | Render a `<Notice tone="warning">` when it occurs            |
| Reject a combination                  | Set `error` on the field. A non-empty error blocks submit    |

<Tip>
  Ask Catalyst for the behavior, not the code: "only ask for a manager when the request is for someone else." Then check it in the preview.
</Tip>

## Live data inside the form

A form can call one of your workflows while it's open and render the result: stock levels, license counts, an employee's current device, a price.

```tsx theme={null}
import { runWorkflowMemoized } from "serval/forms";
import checkLaptopStock from "@serval/workflows/check-laptop-stock";

const stock = runWorkflowMemoized(checkLaptopStock, { model });

return (
  <Form>
    <Model required />
    {stock?.outOfStock && (
      <Notice tone="warning">That model is back-ordered until {stock.eta}.</Notice>
    )}
  </Form>
);
```

* **Results are cached per set of arguments.** The call runs once for each distinct set. Change the selected model and it runs again. Change an unrelated field and it doesn't run at all. Calling it from the render is cheap, not a request per keystroke.
* **The form stays usable while it's in flight**, then re-renders with the result.

<Note>
  Query workflows should be read-only. They run whenever their arguments change, including in previews, so anything that creates or modifies belongs in the submit workflow instead.
</Note>

Every run a form starts, query runs included, appears under **Run history**.

## The submit workflow

```tsx theme={null}
<Form submit={{ workflow: orderLaptop, args: { model, justification, neededBy } }}>
```

On submit, Serval validates every field, starts the workflow with your arguments, and moves the form to its submitted state.

You pass an explicit `args` object rather than the whole form, so the workflow's inputs are a contract you control: rename a field without breaking the workflow, or pass derived values instead of raw answers.

A form doesn't need a submit workflow. One without it collects and validates answers and stops there, which suits an acknowledgment or informational form.

## Safety

Form code runs on Serval's servers in an isolated sandbox with no network access, no filesystem, and no ambient credentials. The only way a form causes anything to happen is by naming one of your team's workflows, and every call is re-resolved and re-authorized on the server. A form can never reach further than the workflows the team already has.

## Finding the forms attached to a workflow

Open a workflow and look for the forms panel. It lists each form that calls the workflow and whether it does so while the form is open or on submit, so you can see the blast radius before changing it.

***

<CardGroup cols={2}>
  <Card title="Share and fill" icon="share-nodes" href="/sections/documentation/forms/share-and-fill">
    Catalog, tickets, Slack, and audience control.
  </Card>

  <Card title="Manage forms" icon="clock-rotate-left" href="/sections/documentation/forms/manage-forms">
    Versions, run history, folders, and copies across teams.
  </Card>
</CardGroup>
