Conditional questions#

In this tutorial you will build a form that shows extra questions only when a previous answer matches a specific value.

This is useful for branching forms where some options require additional configuration—for example, enabling a feature and then asking for its settings.

What you will build#

A deployment configuration form that:

  1. Asks which authentication provider to use (none or oidc).

  2. Shows two extra questions—OIDC server URL and client ID—only when the user selects oidc.

Prerequisites#

How conditional questions work#

TUI Forms uses the standard JSONSchema allOf / if / then pattern.

"allOf": [
  {
    "if":   { "properties": { "key": { "const": "expected_value" } } },
    "then": { "properties": { "extra_field": { <question schema> } } }
  }
]

When the user's answer for key equals expected_value, the questions inside then.properties become active. Otherwise they are skipped entirely—they will not appear in the returned answers dict.

The if condition must follow the pattern {properties: {key: {const: value}}}. Multiple key–value pairs in a single if block are supported; all conditions must match for the then questions to become active.

Step 1—Define the base question#

Start with the choice question that controls the branching:

schema = {
    "title": "Deployment configuration",
    "properties": {
        "auth_provider": {
            "type": "string",
            "title": "Authentication provider",
            "default": "none",
            "oneOf": [
                {"const": "none", "title": "None"},
                {"const": "oidc", "title": "OpenID Connect (OIDC)"},
            ],
        },
    },
}

Step 2—Add the conditional block#

Append an allOf list at the top level of the schema (alongside properties). Each item has an if condition and a then block with the extra questions:

schema = {
    "title": "Deployment configuration",
    "properties": {
        "auth_provider": {
            "type": "string",
            "title": "Authentication provider",
            "default": "none",
            "oneOf": [
                {"const": "none", "title": "None"},
                {"const": "oidc", "title": "OpenID Connect (OIDC)"},
            ],
        },
    },
    "allOf": [
        {
            "if": {
                "properties": {"auth_provider": {"const": "oidc"}}
            },
            "then": {
                "properties": {
                    "oidc_server_url": {
                        "type": "string",
                        "title": "OIDC server URL",
                        "description": "for example, https://auth.example.com/realms/myrealm",
                        "default": "",
                    },
                    "oidc_client_id": {
                        "type": "string",
                        "title": "Client ID",
                        "default": "my-app",
                    },
                }
            },
        }
    ],
}

Step 3—Run the form#

from tui_forms import create_renderer

renderer = create_renderer("stdlib", schema)
answers = renderer.render()
print(answers)

Step 4—Try both branches#

Run the script and select None first:

{'auth_provider': 'none'}

The OIDC questions are skipped; they do not appear in the answers at all.

Run again and select OpenID Connect (OIDC):

{'auth_provider': 'oidc', 'oidc_server_url': 'https://auth.example.com/realms/myrealm', 'oidc_client_id': 'my-app'}

This time both extra questions were asked and their answers are in the dict.

Multiple conditional branches#

You can add more allOf items to handle additional choices. Each item is evaluated independently:

"allOf": [
    {
        "if": {"properties": {"auth_provider": {"const": "oidc"}}},
        "then": {
            "properties": {
                "oidc_server_url": {"type": "string", "title": "OIDC server URL"},
                "oidc_client_id":  {"type": "string", "title": "Client ID"},
            }
        },
    },
    {
        "if": {"properties": {"auth_provider": {"const": "saml"}}},
        "then": {
            "properties": {
                "saml_metadata_url": {"type": "string", "title": "SAML metadata URL"},
            }
        },
    },
]

Conditionally overriding existing questions#

You can also use allOf to override properties already defined in the base properties block. This is useful for conditionally hiding a field or changing its default value.

Hiding a question#

If you have a question that should only be asked in certain modes, you can define it normally and then override it with format: computed when it should be hidden:

schema = {
    "properties": {
        "advanced_settings": {
            "type": "boolean",
            "title": "Enable advanced settings?",
            "default": False
        },
        "expert_mode": {
            "type": "boolean",
            "title": "Enable expert mode?",
            "default": False
        }
    },
    "allOf": [
        {
            "if": {"properties": {"advanced_settings": {"const": False}}},
            "then": {
                "properties": {
                    "expert_mode": {
                        "format": "computed",
                        "default": False
                    }
                }
            }
        }
    ]
}

In this case, expert_mode is only asked if the user selects Yes for advanced_settings. Otherwise, it is computed as False and skipped.

Relocation behavior#

When you override a property in an allOf block, TUI Forms automatically moves it in the wizard flow to appear right after the question that gates it. This ensures that the user answers dependencies in the correct order.

Next steps#