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

# Steps and logic

> Organise elements into steps, and branch the form on what contacts answer

A form is a list of steps. Each step is one screen the contact sees, holding the elements that collect data. Splitting a form into steps keeps each screen short and lets you skip whole sections that don't apply.

## Declaring a step

```json theme={null}
{
  "id": "company_information",
  "label": "Company information",
  "elements": [
    { "key": "company_name", "type": "text", "title": "Company name" },
    { "key": "vat_number", "type": "text", "title": "VAT number" }
  ]
}
```

| Property   | Type    | Description                                                    |
| ---------- | ------- | -------------------------------------------------------------- |
| `id`       | String  | Identifier for the step.                                       |
| `label`    | String  | Step name shown in the progress indicator. Optional.           |
| `enabled`  | Boolean | Whether the step is part of the form. Defaults to `true`.      |
| `elements` | Array   | The [elements](/forms/form-templates/elements) on this screen. |
| `meta`     | Object  | Free-form data attached to the step. Ignored by the engine.    |

## Submitting a step

A step needs a way to move forward. You don't have to declare it: when a step contains no `submit` element, one is appended automatically.

Declare a `submit` element yourself when you want to control its caption:

```json theme={null}
{ "type": "submit", "label": "Continue" }
```

The automatic button is skipped when the last element of the step has `submit_on_change: true` — answering that element submits the step on its own. That is the pattern for a one-question step that branches the rest of the form.

## Conditional steps

Wrap a step in an `:if` / `:then` block to include it only when a condition holds:

```json theme={null}
{
  ":if": { ":cmp": "{data.account_type}", ":eq": "business" },
  ":then": {
    "id": "business_details",
    "label": "Business details",
    "elements": [
      { "key": "company_registration", "type": "text", "title": "Registration number" }
    ]
  }
}
```

The same wrapper works around individual elements, so you can hide a single field rather than a whole screen. See [Conditional elements](/forms/form-templates/elements#conditional-elements).

Conditions are written in penscript — the full operator list is in the [logic reference](/penscript/logic).

<Tip>
  Prefer a conditional step over a long screen of conditional fields. A contact
  who sees a screen half-empty because most of its fields were hidden reads it
  as a broken form.
</Tip>

## Variables

Variables are values defined once and reused across the whole form — static constants, or values computed from answers.

### Defining variables

Without dependencies, use an object:

```json theme={null}
{
  "variables": {
    "company_email": "support@company.com",
    "max_upload_size": 10485760
  }
}
```

When one variable depends on another, use an array — entries are resolved in order:

```json theme={null}
{
  "variables": [
    { "base_price": 100 },
    { "tax_rate": 0.2 },
    { "total_price": "{base_price} * (1 + {tax_rate})" }
  ]
}
```

### Using variables

Reference a variable anywhere in the form with `{variable_name}`:

```json theme={null}
{
  "type": "paragraph",
  "content": "Any question? Write to {company_email}."
}
```

<Warning>
  `user`, `data`, and any name starting with `$` are reserved. Do not use them
  as variable names.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Elements" icon="input-text" href="/forms/form-templates/elements">
    Every element type and its options
  </Card>

  <Card title="Portal pages" icon="browser" href="/forms/form-templates/portal-pages">
    Welcome, review and ending screens
  </Card>

  <Card title="Penscript" icon="code" href="/penscript/introduction">
    The expression language behind conditions and variables
  </Card>

  <Card title="Form templates" icon="file-lines" href="/forms/form-templates">
    Back to form templates
  </Card>
</CardGroup>
