> ## 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.

# Create a form

> Describe a form to Catalyst, preview it live, and publish it to the service catalog.

<Frame caption="Building a form with Catalyst. The workspace on the right previews it live.">
  <img src="https://mintcdn.com/serval/x-XapdxK5rS-MS0N/images/serval-docs/forms/form-workspace.png?fit=max&auto=format&n=x-XapdxK5rS-MS0N&q=85&s=077d2fa88d470caf3fc521a7e3a593f5" alt="Catalyst chat on the left describing a laptop request form, with the form workspace on the right showing version v3 Published, the Check Laptop Stock workflow that runs inside the form, and a live preview of the laptop card grid" width="2836" height="2130" data-path="images/serval-docs/forms/form-workspace.png" />
</Frame>

## Build it

<Steps>
  <Step title="New form">
    Go to **Service Management → Forms → New form**. Serval creates an **Untitled form** and opens the workspace.
  </Step>

  <Step title="Describe what you want">
    Be specific about the questions, the options, and what happens on submit:

    > Create a laptop request form. Options should be MacBook Air, MacBook Pro 14-inch, Lenovo ThinkPad, and Dell Latitude, shown as cards with product photos. Require a business justification. Let people optionally say who it's for and when they need it. On submit, file an IT ticket with the chosen model.
  </Step>

  <Step title="Refine it">
    Keep talking to Catalyst: "make the justification field longer," "only ask for a manager if it's for someone else." Each change re-renders the preview.
  </Step>
</Steps>

<Tip>
  Name the form before publishing. The name is what employees see in the catalog and what the Help Desk Agent reads to decide the form fits a request. "Laptop Request" beats "IT Form 2."
</Tip>

## The workspace

| Area                        | What it does                                                                                                                     |
| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| **Preview**                 | The real form, rendered by the real engine                                                                                       |
| **Can be opened by**        | Which employees may open and submit it. See [audience](/sections/documentation/forms/share-and-fill#control-who-can-open-a-form) |
| **Runs inside the form**    | Workflows called while the form is open, to fetch live data                                                                      |
| **Runs on submit**          | The workflow that receives the answers                                                                                           |
| **Version selector**        | Switch versions, and see which one is published                                                                                  |
| **Publish**                 | Deploys the current draft                                                                                                        |
| **Run history**             | Every session, and the runs it started                                                                                           |
| **Open in service catalog** | The published form, exactly as employees see it                                                                                  |
| **Code (`</>`)**            | The form's source                                                                                                                |

## Preview and publish

The preview is a live session against your draft. Conditional fields appear, validation fires, and live-data workflows run.

<Warning>
  A preview submit runs the latest submit workflow **for real**, even as an unpublished draft. It doesn't publish your drafts, but side effects still happen: a ticket, provisioned access, a message.
</Warning>

Edits autosave to a draft. The action bar shows **Unpublished changes** when your draft differs from what's live. Click **Publish** to deploy it.

<Note>
  Publishing never disturbs a submission in progress. A session stays on the version it opened against.
</Note>

If a publish fails, the error names the problem in the code: an unknown option, an invalid icon name, a workflow reference that doesn't resolve.

## The code behind a form

Catalyst writes it, and most builders never open it. It's under the `</>` button when you want it.

```tsx theme={null}
import { view, field, Form } from "serval/forms";
import orderLaptop from "@serval/workflows/order-laptop";

export const main = view({
  fn: () => {
    const [model, Model] = field.cards("model", [
      { value: "mba", label: "MacBook Air (M-series)", description: "Lightweight macOS laptop for everyday work and travel." },
      { value: "mbp", label: "MacBook Pro 14-inch", description: "High-performance macOS laptop for engineering and design." },
    ], { label: "Choose your laptop" });

    const [justification, Justification] = field.text("justification", {
      label: "Business justification",
      multiline: true,
      maxLength: 500,
    });

    return (
      <Form
        header={{ title: "Laptop Request", subtitle: "Request a new laptop from IT", icon: "laptop" }}
        submit={{ workflow: orderLaptop, args: { model, justification } }}
      >
        <Model required />
        <Justification required />
      </Form>
    );
  },
});
```

* **`field.<type>(key, …)`**: declares a question, returns `[value, Component]`
* **`<Form>`**: the root. `header` sets the title band, `submit` names the workflow, and `accent` / `density` / `surface` pick the look
* **Placement props**: `required`, `readOnly`, and `error` go on the component, not the declaration

Because the value is an ordinary variable, logic is ordinary code: `{model === "mbp" && <Justification required />}`.

<Note>
  Form code runs in a sandbox with no network, no filesystem, and no credentials. The only way it can cause work is by naming one of your workflows, and every call is re-authorized on the server.
</Note>

***

<CardGroup cols={2}>
  <Card title="Fields and layout" icon="list-check" href="/sections/documentation/forms/fields">
    The field-type and validation reference.
  </Card>

  <Card title="Logic and workflows" icon="code-branch" href="/sections/documentation/forms/logic-and-workflows">
    Conditional questions, live data, and the submit handler.
  </Card>
</CardGroup>
