How to Configure Dynamic Forms

Feature Overview

The dynamic form configuration feature allows you to declaratively configure a friendly interactive experience for resources such as Pipeline and Task. Without writing frontend code, simply by adding style.tekton.dev/descriptors configuration in the resource's annotations, you can achieve:

  • Dynamic form generation: Automatically generate interactive forms for resource parameters, providing a better experience when orchestrating and executing Pipelines and Tasks.
  • Rich component support: Provides various form components such as text boxes, selectors, switches, YAML editors, etc.
  • Field validation: Built-in validation rules to ensure user input meets requirements.
  • Dynamic data fetching: Supports dynamically loading option data through APIs.
  • Field links: Adds product or external links to specific fields.
  • Metadata-only configuration: Adds labels, descriptions, tooltips, disabled state, preview, or links without overriding the field's default component.
  • Structured array editing: Splits each item of a string array into one or more editable fields.
  • Combined inputs: Presents several related controls for one Pipeline parameter and combines their values before execution.
  • Nested forms: Loads the configuration of a selected resource and presents its parameters inside the current form.
  • Localized metadata: Maintains display names, descriptions, and tooltips in multiple languages while preserving the original Tekton names and values.

Dynamic form behavior is reused across the main Pipeline workflows:

Product areaHow dynamic forms are used
Pipeline and Task authoringConfigure parameter controls and maintain display metadata for Params, Workspaces, Results, and Tasks.
Pipeline and Task detailsDisplay business-friendly labels, descriptions, and tooltips while keeping the original Tekton names visible.
PipelineRun, TaskRun, and Run Template executionReuse the selected Pipeline or Task descriptors for selectors, structured editors, nested forms, previews, and runtime settings.
Integration configurationUse descriptors supplied by the selected ResourceInterface or Connector for Integration-specific controls and options.
Trigger and ScheduledTrigger configurationReuse the target Run Template's run configuration. Event variables are configured through the TriggerBinding and the selected start method.

If a field does not have a supported descriptor, the product keeps its normal input control and native Tekton behavior.

Use Cases

  • Pipeline orchestration/runtime: Allow users to fill in parameters such as namespace and secrets through friendly forms when orchestrating or triggering Pipeline
  • Task reuse: Provide default values, dropdown options, and validation for common parameters at the Task or Pipeline template level
  • Multi-environment selection: Dynamically query available environments or configurations from APIs and use them as selection options
  • Structured configuration: Edit values such as KEY=VALUE, JSON strings, or other structured array items without manually formatting each string
  • Resource-backed configuration: Select a resource and configure its parameters inline, such as values for a selected template
  • Consistent Pipeline authoring and execution: Reuse the same parameter metadata and dynamic controls in Pipeline editing, Task configuration, PipelineRun, and TaskRun forms

Quick Start

Basic Configuration Structure

All configurations are defined through the style.tekton.dev/descriptors field in annotations:

metadata:
  annotations:
    style.tekton.dev/descriptors: |
      - path: params.parameter-name
        x-descriptors:
          - configuration-item1
          - configuration-item2

Configuration Description

  • path: Use params.<parameter-name>, workspaces.<workspace-name>, or results.<result-name> according to the resource field being described.
  • x-descriptors: Configuration item array, each configuration item starts with urn:alm:descriptor:

Simple Example

Configure a required text input box for a Task parameter:

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: simple-task
  annotations:
    style.tekton.dev/descriptors: |
      - path: params.gitURL
        x-descriptors:
          - urn:alm:descriptor:label:zh:GitRepo
          - urn:alm:descriptor:com.tectonic.ui:text
          - urn:alm:descriptor:placeholder:zh:Git URL
          - urn:alm:descriptor:com.tectonic.ui:validation:required
spec:
  params:
    - name: gitURL
      type: string
  steps:
    - name: clone
      image: alpine/git
      script: |
        git clone $(params.gitURL)

Capability Selection Guide

Use the following table to choose the smallest descriptor set that matches your requirement.

RequirementRecommended ConfigurationExplicit Component Descriptor
Change field label, description, or tooltip onlyBasic metadata descriptorsNot required
Keep the default input behavior but add field metadata or linksMetadata-only configurationNot required
Render a specific input control, such as text, textarea, select, switch, or YAML editorComponent type descriptorsRequired
Provide fixed or expression-based defaultsDefault value descriptorsUsually required
Validate user inputValidation descriptorsRequired
Load select options from an APISelect component with dynamic optionsRequired
Preview referenced resource contentPreview descriptorsNot required; can combine with one
Add a field link to product guidance or an external URLLink descriptorsNot required; can combine with one
Split one parameter into multiple sub-inputsCombine componentRequired
Edit each item of a string array as structured fieldsArray table componentRequired
Show the parameters of a selected resource inside the current fieldInline form componentRequired
Maintain display metadata in multiple languagesDescriptor metadata for Params, Workspaces, and ResultsNot required

Basic Metadata

Form Label

# Label for the form
- urn:alm:descriptor:label:en:English Name
- urn:alm:descriptor:label:zh:Chinese name

Form Description

# Description for the form
- urn:alm:descriptor:description:en:English Description
- urn:alm:descriptor:description:zh:Chinese Description

Placeholder Hint

# Placeholder for the form
- urn:alm:descriptor:placeholder:en:This is a Placeholder
- urn:alm:descriptor:placeholder:zh:This is a Placeholder

Form Help Tooltip

# Help tooltip for the form
- urn:alm:descriptor:tooltip:en:This is a Help Tip
- urn:alm:descriptor:tooltip:zh:This is a Help Tip

Form Disabled State

# Whether the form is disabled
- urn:alm:descriptor:com.tectonic.ui:disabled

Metadata-only Configuration Without a Component

You can configure field metadata and enhancements without declaring a form component such as text, select, or widgets:select.

In this mode, the UI keeps the parameter entry's default rendering component and only applies the supported descriptor enhancements. This is useful when you want to improve the display text or add help links without changing the parameter's input behavior.

- path: params.mailSubject
  x-descriptors:
    - urn:alm:descriptor:label:en:Mail Subject
    - urn:alm:descriptor:label:zh:Mail Subject
    - urn:alm:descriptor:description:en:Used to render the notification title.
    - urn:alm:descriptor:description:zh:Used to render the notification title.
    - urn:alm:descriptor:tooltip:en:Rendered at runtime.
    - urn:alm:descriptor:tooltip:zh:Rendered at runtime.

Supported descriptors in metadata-only mode:

  • urn:alm:descriptor:label:*
  • urn:alm:descriptor:description:*
  • urn:alm:descriptor:tooltip:*
  • urn:alm:descriptor:com.tectonic.ui:disabled
  • urn:alm:descriptor:enhance:preview:* and urn:alm:descriptor:preview:*
  • urn:alm:descriptor:enhance:docLink:* and urn:alm:descriptor:docLink:*

The following descriptors require an explicit component capability and do not apply in metadata-only mode:

  • urn:alm:descriptor:placeholder:*
  • urn:alm:descriptor:com.tectonic.ui:validation:*
  • urn:alm:descriptor:expression:props.default*
  • urn:alm:descriptor:expression:props.options:*
  • urn:alm:descriptor:widgets:*

If you need placeholders, validation, dynamic options, default values, or a specific widget behavior, declare the corresponding component explicitly.

To make a metadata-only field read-only, add the disabled descriptor:

- urn:alm:descriptor:com.tectonic.ui:disabled

Advanced and basic form modes

For parameters with a supported dynamic form descriptor, the product can show an Advanced form that uses the configured control or widget. A Basic form remains available as a fallback when the advanced control is not needed or cannot be used in the current context.

The actual Pipeline parameter value is unchanged by switching modes. The mode only changes how the value is edited. This lets users use a friendly selector or structured editor when it is available, while retaining a direct input method for troubleshooting or exceptional values.

When a parameter is already supplied by a Pipeline Integration, the form can show the Integration configuration first. After the Integration binding is removed, the parameter can return to its descriptor-defined dynamic control.

Descriptor Metadata for Params, Workspaces, and Results

Dynamic form metadata is not limited to parameter input controls. Pipeline authors can also configure the display name, description, and tooltip for Pipeline Params, Workspaces, and Results.

Open the Pipeline configuration form, select a Param, Workspace, or Result, and open Advanced Form Configuration. You can maintain the metadata in Form mode or edit the complete descriptor in YAML mode.

The metadata is reused in:

  • Pipeline and Task details.
  • Pipeline and Task execution forms.
  • Pipeline task configuration.
  • Result variable suggestions.

The original Tekton name remains visible together with the configured display name, and references inserted into Pipeline definitions continue to use the original Tekton name. For example, a Result displayed as Artifact URL still inserts $(tasks.build.results.artifact-url).

Multiple languages

The Form editor uses English by default. A language selector appears when the descriptor contains metadata in more than one language. You can select a language and update its display name, description, and tooltip without overwriting the other languages.

Use YAML mode when you need to maintain the complete descriptor, including component, validation, dynamic option, or widget settings. Form mode is intended for common display metadata and keeps the rest of the descriptor configuration intact.

Workspace and Result boundaries

Workspace and Result descriptors enhance display information only. They do not turn a Workspace or Result into an input control, and their component, validation, default, dynamic option, and widget settings are not applied. Workspace and Result required or optional behavior continues to come from the Pipeline declaration.

Run Template behavior

Run Template uses the Pipeline descriptor as the base configuration for its parameters. A Run Template can provide its own descriptor configuration when a specific template needs different labels, controls, defaults, or visibility.

When editing a Run Template, all Pipeline parameters are available for configuration. Use Set at Runtime to identify values that should be supplied when the template is run. During execution, users see the parameters that the Run Template exposes for runtime input; fixed values remain part of the saved template.

Changing a Pipeline descriptor does not automatically replace a Run Template's specific configuration. Review the Run Template after changing the Pipeline, especially when adding parameters or changing their runtime behavior.

The descriptor source depends on the form context. Pipeline and Task forms read descriptors from the selected Pipeline or Task. Integration forms read Integration descriptors from the selected ResourceInterface or Connector. Trigger and ScheduledTrigger forms reuse the Run Template configuration; their event-variable values are maintained through the TriggerBinding and the corresponding start method.

Example metadata for a Workspace and Result

- path: workspaces.source
  x-descriptors:
    - urn:alm:descriptor:label:en:Source Workspace
    - urn:alm:descriptor:description:en:Workspace used for source files
    - urn:alm:descriptor:tooltip:en:Provide storage that contains the source code
- path: results.image-url
  x-descriptors:
    - urn:alm:descriptor:label:en:Published Image
    - urn:alm:descriptor:description:en:Image produced by the Pipeline
    - urn:alm:descriptor:tooltip:en:Use this Result in downstream tasks

These descriptors improve presentation only. The Pipeline still uses the original source Workspace and image-url Result names in its definitions and variable references.

Component Types

Text Input

# value: string
- urn:alm:descriptor:com.tectonic.ui:text

Multi-line Text Input

# value: string
- urn:alm:descriptor:com.tectonic.ui:textarea

Tags Input

# value: array
- urn:alm:descriptor:com.tectonic.ui:tagsInput

Radio

# Declare a radio option with value "option-a"
- urn:alm:descriptor:com.tectonic.ui:radio:option-a
# Set the English display label of option-a to "Option A"
- urn:alm:descriptor:com.tectonic.ui:radio:option-a:en:Option A
# Set the Chinese display label of option-a to "Option A(zh)"
- urn:alm:descriptor:com.tectonic.ui:radio:option-a:zh:Option A(zh)
# Declare a radio option with value "option-b"
- urn:alm:descriptor:com.tectonic.ui:radio:option-b
# Set the English display label of option-b to "Option B"
- urn:alm:descriptor:com.tectonic.ui:radio:option-b:en:Option B
# Set the Chinese display label of option-b to "Option B(zh)"
- urn:alm:descriptor:com.tectonic.ui:radio:option-b:zh:Option B(zh)
# Set the default selected option to "option-a"
- urn:alm:descriptor:com.tectonic.default:option-a

Switch

# value: boolean
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch

# Map value to specified string
# When switch is on, value is True
- urn:alm:descriptor:props:booleanSwitch:true:True
# When switch is off, value is False
- urn:alm:descriptor:props:booleanSwitch:false:False

YAML Editor

# value: string
- urn:alm:descriptor:com.tectonic.ui:yaml

Select Box

Basic Content Configuration

# value: string
- urn:alm:descriptor:com.tectonic.ui:select
# Options
- urn:alm:descriptor:props:select:options:valueA
- urn:alm:descriptor:props:select:options:valueB
# Set the select box to multiple selection mode
# value: array
- urn:alm:descriptor:props:select:multiple
# Manually entered content can be used as selection items
- urn:alm:descriptor:props:select:allowCreate
# Content can be cleared
- urn:alm:descriptor:props:select:clearable

API Dynamic Options Configuration

When form controls need to dynamically load available options based on the current environment (such as available namespaces, secrets, etc.), you can use API dynamic configuration to retrieve dropdown options from backend interfaces in real-time.

In this mode, API URLs are typically used in combination with context variables to dynamically construct request paths based on the user's current cluster, namespace, project, or filled parameters. For example:

# Use ${context.cluster} variable in URL to dynamically retrieve namespace list for current cluster
- urn:alm:descriptor:expression:props.options:api:apiPath
- urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/api/v1/namespaces

Available Context Variables

In dynamic API paths, you can use ${variable} to reference runtime context information.

  • General Context Variables
VariableDescription
${context.cluster}Current cluster name
${context.namespace}Current namespace
${context.project}Current project
${current.search}Get the current search input from the dropdown box
  • Pipeline/Task Related Context Variables
VariableDescription
${context.params}Parameters object, e.g., ${context.params.repo} can reference the current value of the repo parameter in the form

For Integration forms, ${context.connectorClass} is the selected ConnectorClass object. Schema expressions can access the same object as context.connectorClass, for example when resolving an OpenAPI schema from the ConnectorClass definition.

These variables are typically used in combination with API URLs to dynamically construct request addresses in different environments, enabling environment-aware option loading logic. For example:

- urn:alm:descriptor:expression:props.options:api:apiPath
- urn:alm:descriptor:expression:props.options:api:/api/v1/${context.namespace}/deployments?repo=${context.params.repo}

Configure Data Path for Returned Data

- urn:alm:descriptor:expression:props.options:path:spec.items

When the API returns a complex structure, for example:

{
  "spec": {
    "items": [ ... ]
  }
}

You can use path to specify how to extract the options array from the structure.

Configure Label and Value Mapping Fields for Options

If the returned data object is complex and requires specifying inner fields as "display name" and "option value", you can use:

- urn:alm:descriptor:expression:props.options:label:path:metadata.name
- urn:alm:descriptor:expression:props.options:value:path:metadata.name

When the API returns a simple string[] array, this configuration is not needed.

Configure API Query Parameters

If you need to pass query parameters to the API request (such as search keywords or filters), you can use:

# Add query parameter named "search" with user input as value
- urn:alm:descriptor:widgets:select
- urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/apis/tekton.dev/v1/namespaces/${context.namespace}/pipelineruns
- urn:alm:descriptor:expression:props.options:path:items
- urn:alm:descriptor:expression:props.options:label:path:metadata.name
- urn:alm:descriptor:expression:props.options:value:path:metadata.name
- urn:alm:descriptor:expression:props.options:api:params:search:${current.search}

When user types "test" in the dropdown, the actual request will be:

/kubernetes/.../pipelineruns?search=test

Schema-based options

In Integration scenarios, a select can use an OpenAPI schema exposed by the selected Connector instead of describing each API request and option mapping manually:

- urn:alm:descriptor:widgets:select
- urn:alm:descriptor:expression:props.options:schema:openapi:context.connectorClass.spec.api.openapi:listProjects

The platform uses the schema to build the available options and their form behavior. This is useful when the Connector provides a standard API for projects, branches, tags, or merge requests.

Computed option fields

For more complex responses, use expressions to calculate the option list or each option's label, value, and description:

- urn:alm:descriptor:expression:props.options:path.exp:$.items.flatMap(item => item.spec.targets || [])
- urn:alm:descriptor:expression:props.options:label:path.exp:`${$.name} (${$.status})`
- urn:alm:descriptor:expression:props.options:value:path.exp:$.id
- 'urn:alm:descriptor:expression:props.options:description:path.exp:`Target: ${$.name}`'

The expression receives the current option as $. Expressions can also use the current form context, such as context.params, to calculate a value.

Advanced Usage: Supports JavaScript expressions, for example:

${Object.keys(context.params || {}).join(',')}

Enhancements

Parameter Default Values

Fixed Default Value

- urn:alm:descriptor:expression:props.default:default-value

Expression-based Dynamic Default Value

Available variables:

  • option: Option raw data

  • index: Option index

  • length: Total number of options

  • context: Context parameters

    # Select options with specific labels
    - urn:alm:descriptor:expression:props.default.exp:option.metadata.labels['default']==='true'
    # Default to select the first item
    - urn:alm:descriptor:expression:props.default.exp:index === 0
    # Default to select the last item
    - urn:alm:descriptor:expression:props.default.exp:index === length - 1
    # Default to select when there is only one option
    - urn:alm:descriptor:expression:props.default.exp:length === 1
    # Default to select the current namespace
    - urn:alm:descriptor:expression:props.default:${context.namespace}
    # Complex conditions
    - urn:alm:descriptor:expression:props.default.exp:option.metadata.name.includes('test') && option.status.conditions.some(c => c.status==='True')
INFO

For conditional syntax rules in complex scenarios, please refer to JavaScript syntax.

Form Validation

Supports configuring various validation rules, multiple rules can be applied simultaneously.

Required Validation

- urn:alm:descriptor:com.tectonic.ui:validation:required

Length Validation

# Minimum length
- urn:alm:descriptor:com.tectonic.ui:validation:minLength:6
# Maximum length
- urn:alm:descriptor:com.tectonic.ui:validation:maxLength:64

Numeric Range Validation

# Minimum value (applicable to numeric types)
- urn:alm:descriptor:com.tectonic.ui:validation:minimum:1
# Maximum value
- urn:alm:descriptor:com.tectonic.ui:validation:maximum:100

Regular Expression Validation

# Positive integer
- urn:alm:descriptor:com.tectonic.ui:validation:pattern:[1-9]\d*
# Kubernetes resource name (lowercase letters, numbers, hyphens)
- urn:alm:descriptor:com.tectonic.ui:validation:pattern:[a-z0-9]([-a-z0-9]*[a-z0-9])?

Combined Example: Configure a password field with complete validation

- path: params.password
  x-descriptors:
    - urn:alm:descriptor:label:zh:password
    - urn:alm:descriptor:com.tectonic.ui:text
    - urn:alm:descriptor:description:zh:At least 8 characters, including letters and numbers.
    - urn:alm:descriptor:com.tectonic.ui:validation:required
    - urn:alm:descriptor:com.tectonic.ui:validation:minLength:8
    - urn:alm:descriptor:com.tectonic.ui:validation:maxLength:32

Preview Component

The Preview Component allows users to preview the YAML content of a selected resource directly from the form. This is particularly useful in scenarios where users need to verify resource configurations before using them in Pipelines, such as checking ConfigMap content, validating Secret data, or reviewing Task/Pipeline YAML definitions.

For example, when a user selects a ConfigMap from a dropdown to use as a volume mount or environment variable source in a Task, they can preview the ConfigMap's data directly in the form to ensure the correct resource is selected.

Configuration

# Enable preview - show button when parameter has value
- urn:alm:descriptor:enhance:preview:context.params.<param-name>

# API endpoint to fetch resource content
- urn:alm:descriptor:preview:resource:api:<api-url>

# JSONPath or JS expression to extract content from response
- urn:alm:descriptor:preview:resource:path:<jsonpath>
- urn:alm:descriptor:preview:resource:path.exp:<js-expression>

Example: Configure a dropdown to select a ConfigMap, with a preview button to view its YAML content.

- path: params.configmap
  x-descriptors:
    # Form label
    - urn:alm:descriptor:label:en:ConfigMap
    - urn:alm:descriptor:label:zh:ConfigMap

    # Dropdown to select ConfigMap from current namespace
    - urn:alm:descriptor:com.tectonic.ui:select
    - urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/api/v1/namespaces/${context.namespace}/configmaps
    - urn:alm:descriptor:expression:props.options:path:items
    - urn:alm:descriptor:expression:props.options:label:path:metadata.name
    - urn:alm:descriptor:expression:props.options:value:path:metadata.name
    - urn:alm:descriptor:com.tectonic.ui:validation:required

    # Preview feature
    - urn:alm:descriptor:enhance:preview:context.params.configmap
    - urn:alm:descriptor:preview:resource:api:/kubernetes/${context.cluster}/api/v1/namespaces/${context.namespace}/configmaps?fieldSelector=metadata.name%3D${context.params.configmap}
    - urn:alm:descriptor:preview:resource:path:items[0]

How it works in the UI:

  1. User selects a ConfigMap from the dropdown (e.g., my-config).
  2. A preview button appears next to the dropdown.
  3. Clicking the preview button fetches the selected ConfigMap from the Kubernetes API.
  4. The raw YAML content is displayed in a preview panel with syntax highlighting.

The preview request can use GET or POST. A POST preview can build a PipelineRun or TaskRun request from the current form values and show the result returned by the server.

Preview content can be displayed as:

  • YAML or another resource representation.
  • An article with a subject and HTML body, such as a rendered notification message.

Preview is intended for checking the result before execution. Runtime-only variables that are unavailable in the preview context may remain unresolved, so the preview should be treated as a reference rather than a guarantee of the final execution output.

The Link Configuration adds a clickable link to a specific field. Use it when users need quick access to related guidance, a product document, an external reference page, or another system URL while filling in a parameter.

Links can be used together with a form component, or in metadata-only mode when you only want to add help text and a link without changing the field's default input component.

Configuration

# Enable an internal product link
- urn:alm:descriptor:enhance:docLink:productDoc

# Product link path. Product documentation pages are a common use case.
- urn:alm:descriptor:docLink:link:alauda-devops-pipelines/pipelines/how_to/configure_dynamic_forms.html

# Optional link tooltip label. The UI uses a built-in default when omitted.
- urn:alm:descriptor:docLink:label:en:Open guide
- urn:alm:descriptor:docLink:label:zh:Open guide

For a full URL link, use the external link type and provide a full http or https URL:

- urn:alm:descriptor:enhance:docLink:external
- urn:alm:descriptor:docLink:link:https://example.com/docs
- urn:alm:descriptor:docLink:label:en:External reference
- urn:alm:descriptor:docLink:label:zh:External reference

Example: Add a link to a parameter guide for a Git repository URL field.

- path: params.gitURL
  x-descriptors:
    - urn:alm:descriptor:label:en:Git Repository URL
    - urn:alm:descriptor:label:zh:Git Repository URL
    - urn:alm:descriptor:description:en:Enter the source repository URL.
    - urn:alm:descriptor:description:zh:Enter the source repository URL.
    - urn:alm:descriptor:com.tectonic.ui:text
    - urn:alm:descriptor:enhance:docLink:productDoc
    - urn:alm:descriptor:docLink:link:alauda-devops-pipelines/pipelines/how_to/configure_dynamic_forms.html
    - urn:alm:descriptor:docLink:label:en:Open parameter guide
    - urn:alm:descriptor:docLink:label:zh:Open parameter guide

Configuration Rules

  • Use only one link type for a field: productDoc or external.
  • productDoc uses a product link path, for example alauda-devops-pipelines/pipelines/how_to/configure_dynamic_forms.html.
  • external only accepts full http or https URLs.
  • If the link is invalid or the type does not match the link value, the UI does not render the link.

Advanced Widgets

In complex CI/CD scenarios, the following advanced form components can help you handle more sophisticated configuration requirements.

Combine Component

In complex CI/CD scenarios, you may encounter situations where a single array parameter needs to collect data from multiple different sources (different APIs). For example, a workloads parameter might need to include both "Deployments" and "StatefulSets", which require fetching data from entirely different Kubernetes APIs.

To achieve this, you can use the Combine Component (widgets:combine). It allows you to split a single parameter into multiple independent sub-form controls on the UI (e.g., a radio button to select the resource type, and a select box to pick the actual resources), and then combine their values into a final array or JSON object via JavaScript expressions before submitting to the Pipeline.

Configuration

# Declare the use of the combine widget
- urn:alm:descriptor:widgets:combine

# Parent combine logic: defines how to merge all sub-component values into the final parameter
# Available variable: items (contains all sub-component values)
# Example: (items.deployments || []).concat(items.statefulsets || []) - merge multiple arrays into one
- urn:alm:descriptor:combine:value.exp:<js-expression>

# Parent: define sub-components
- urn:alm:descriptor:combine:items:
    - path: <sub-component-name-1>
      x-descriptors:
        # Child split logic: defines how to extract this sub-component's value from the parent for echo display
        # Available variable: parent (the current parameter value)
        # Example: (parent || []).filter(p => p.startsWith('deploy:')) - extract items with 'deploy:' prefix
        - urn:alm:descriptor:combine:value.exp:<js-expression>
        # ... other descriptors for this sub-component

    - path: <sub-component-name-2>
      x-descriptors:
        # Child split logic: defines how to extract this sub-component's value from the parent for echo display
        - urn:alm:descriptor:combine:value.exp:<js-expression>
        # ... other descriptors for this sub-component

For more js-expression syntax, please refer to JavaScript syntax.

Example: Configure a multi-select parameter target_resources that allows users to pick resources from both Deployments and StatefulSets. The final parameter value will be an array combining selections from both APIs, with specific prefixes applied to distinguish their types (e.g., ["deploy:app-v1", "sts:db-v1"]).

- path: params.target_resources
  x-descriptors:
    # Declare the use of the combine widget
    - urn:alm:descriptor:widgets:combine
    
    # [Parent] Combine logic: concat the arrays from both 'deployments' and 'statefulsets' sub-components
    - urn:alm:descriptor:combine:value.exp:(items.deployments || []).concat(items.statefulsets || [])
    
    # Define sub-components
    - urn:alm:descriptor:combine:items:
        
        # Sub-component 1: Select Deployments
        - path: deployments
          x-descriptors:
            # [Child 1] Split logic: extract items starting with 'deploy:' from the parent array
            - urn:alm:descriptor:combine:value.exp:(parent || []).filter(p => p.startsWith('deploy:'))
            - urn:alm:descriptor:label:en:Deployments
            - urn:alm:descriptor:label:zh:Deployments
            - urn:alm:descriptor:widgets:select
            - urn:alm:descriptor:props:select:multiple
            - urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/apis/apps/v1/namespaces/${context.namespace}/deployments
            - urn:alm:descriptor:expression:props.options:path:items
            - urn:alm:descriptor:expression:props.options:label:path:metadata.name
            # Prepend 'deploy:' prefix
            - urn:alm:descriptor:expression:props.options:value:path.exp:'deploy:'+$.metadata.name
            
        # Sub-component 2: Select StatefulSets
        - path: statefulsets
          x-descriptors:
            # [Child 2] Split logic: extract items starting with 'sts:' from the parent array
            - urn:alm:descriptor:combine:value.exp:(parent || []).filter(p => p.startsWith('sts:'))
            - urn:alm:descriptor:label:en:StatefulSets
            - urn:alm:descriptor:label:zh:StatefulSets
            - urn:alm:descriptor:widgets:select
            - urn:alm:descriptor:props:select:multiple
            - urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/apis/apps/v1/namespaces/${context.namespace}/statefulsets
            - urn:alm:descriptor:expression:props.options:path:items
            - urn:alm:descriptor:expression:props.options:label:path:metadata.name
            # Prepend 'sts:' prefix
            - urn:alm:descriptor:expression:props.options:value:path.exp:'sts:'+$.metadata.name

How it works in the UI:

  1. The user sees two separate multi-select boxes simultaneously: "Deployments" and "StatefulSets".

  2. The Deployments box fetches data from the Deployment API, and the StatefulSets box fetches from the StatefulSet API.

  3. When the user selects dummy and alm in Deployments, and db-1 in StatefulSets, the final target_resources array submitted to the Task is naturally combined:

    - name: target_resources
      value:
        - deploy:dummy
        - deploy:alm
        - sts:db-1

Array table component

The Array Table Component (widgets:arrayTable) edits a string array as structured rows. Use it when each array item contains multiple business values, such as KEY=VALUE, name|protocol|description, or a JSON string.

The Pipeline parameter remains a string[]. The form only changes how each item is edited and converts each row back to one string before the Pipeline starts.

- path: params.buildArgs
  x-descriptors:
    - urn:alm:descriptor:widgets:arrayTable
    - urn:alm:descriptor:arrayTable:separator:=
    - urn:alm:descriptor:arrayTable:items:
        - path: key
          x-descriptors:
            - urn:alm:descriptor:label:en:Key
            - urn:alm:descriptor:com.tectonic.ui:text
        - path: value
          x-descriptors:
            - urn:alm:descriptor:label:en:Value
            - urn:alm:descriptor:com.tectonic.ui:text

For MESSAGE=left=middle=right, the first separator divides the key and the remaining text is kept in the value. This prevents additional separators in the last field from being lost.

The layout adapts to the number of row fields:

  • One field: a full-width row without a separate header.
  • Two fields: fields are shown in columns with a shared header.
  • More than two fields: fields are shown vertically with labels in each row.

For JSON or another format that cannot be represented by a separator, define urn:alm:descriptor:arrayTable:parse.exp: and urn:alm:descriptor:arrayTable:stringify.exp:. The parse expression receives parent (the stored string) and context, and must return an object containing all configured item paths. The stringify expression receives items (the current row values) and context, and must return a string.

For example, a JSON array item can be edited as two fields and converted back to the stored string:

- path: params.jsonItems
  x-descriptors:
    - urn:alm:descriptor:widgets:arrayTable
    - urn:alm:descriptor:arrayTable:items:
        - path: key
          x-descriptors:
            - urn:alm:descriptor:label:en:Key
            - urn:alm:descriptor:com.tectonic.ui:text
        - path: value
          x-descriptors:
            - urn:alm:descriptor:label:en:Value
            - urn:alm:descriptor:com.tectonic.ui:text
    - 'urn:alm:descriptor:arrayTable:parse.exp:parse(parent)'
    - 'urn:alm:descriptor:arrayTable:stringify.exp:stringify(items)'

Invalid conversion results are shown as form errors and do not silently overwrite the original value.

Do not use Array Table for a simple list of independent strings. Use the standard array input or tagsInput for that case.

Inline form component

The Inline Form Component (widgets:inlineForm) displays the parameters of a selected resource inside the current parameter field. It is useful when one selection determines the next set of inputs, such as selecting a message template and then entering the template's variables.

The inline form can load its resource from an API or from a value already available in the current form context. The descriptor identifies:

  • The resource to load.
  • The resource path that contains the selected object.
  • The resource's parameter and descriptor paths.
  • How to convert the nested form value to and from the parent parameter value.

Example:

- path: params.templateValues
  x-descriptors:
    - urn:alm:descriptor:widgets:inlineForm
    - urn:alm:descriptor:inlineForm:resource:api:/kubernetes/${context.cluster}/api/v1/namespaces/kube-public/configmaps?fieldSelector=metadata.name%3D${context.params.templateName}
    - urn:alm:descriptor:inlineForm:resource:path:items[0]
    - 'urn:alm:descriptor:inlineForm:params:path.exp:parse($.metadata.annotations["style.tekton.dev/descriptors"] || "")?.map(d => ({type: "string", name: d.path.split("params.")[1]}))'
    - 'urn:alm:descriptor:inlineForm:stringify:$?.length ? stringify($.reduce((acc, cur) => { acc[cur.name] = cur.value; return acc; }, {})) : ""'
    - 'urn:alm:descriptor:inlineForm:parse:Object.entries(parse($ || "") || {}).map(([name, value]) => ({name, value}))'

When the resource or its parameters change, the inline form is refreshed for the current context. Nested validation is propagated to the parent field, so the Pipeline cannot be submitted with an invalid inline configuration.

Inline forms can be combined with Preview Component. For example, users can configure the selected template's variables and then preview the rendered result without leaving the Pipeline form.

Full Examples

Configure Metadata-only Dynamic Forms in Task

Example: Add labels, descriptions, tooltips, and a field link without declaring a form component.

Effect: The UI keeps the default input behavior for each parameter and applies only the configured metadata and link.

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: metadata-only-demo-task
  namespace: <your namespace>
  annotations:
    style.tekton.dev/descriptors: |
      - path: params.mailSubject
        x-descriptors:
          - urn:alm:descriptor:label:en:Mail Subject
          - urn:alm:descriptor:label:zh:Mail Subject
          - urn:alm:descriptor:description:en:Used to render the notification title.
          - urn:alm:descriptor:description:zh:Used to render the notification title.
          - urn:alm:descriptor:tooltip:en:Rendered at runtime.
          - urn:alm:descriptor:tooltip:zh:Rendered at runtime.
      - path: params.runtimeNote
        x-descriptors:
          - urn:alm:descriptor:label:en:Runtime Note
          - urn:alm:descriptor:label:zh:Runtime Note
          - urn:alm:descriptor:description:en:No component descriptor is configured for this parameter.
          - urn:alm:descriptor:description:zh:No component descriptor is configured for this parameter.
          - urn:alm:descriptor:tooltip:en:The UI should keep the parameter entry's default rendering component.
          - urn:alm:descriptor:tooltip:zh:The UI should keep the parameter entry's default rendering component.
          - urn:alm:descriptor:enhance:docLink:productDoc
          - urn:alm:descriptor:docLink:link:alauda-devops-pipelines/pipelines/how_to/configure_dynamic_forms.html
          - urn:alm:descriptor:docLink:label:en:Open form guide
          - urn:alm:descriptor:docLink:label:zh:Open form guide
spec:
  params:
    - name: mailSubject
      type: string
      default: Metadata-only subject
    - name: runtimeNote
      type: string
      default: This parameter has no explicit form component descriptor.
  steps:
    - name: print-values
      image: ubuntu
      env:
        - name: MAIL_SUBJECT
          value: $(params.mailSubject)
        - name: RUNTIME_NOTE
          value: $(params.runtimeNote)
      script: |
        #!/usr/bin/env sh
        set -eu
        echo "mailSubject=${MAIL_SUBJECT}"
        echo "runtimeNote=${RUNTIME_NOTE}"

Configure Dynamic Forms in Task

Example: Provide a dropdown box for selecting namespaces for the image parameter of a Task.

Effect: When users orchestrate a Pipeline or TaskRun based on the UI, the namespace parameter of this Task supports dropdown selection of namespaces.

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: demo-task
  namespace: <your namespace>
  annotations:
    style.tekton.dev/descriptors: |
      - path: params.namespace
        x-descriptors:
          - urn:alm:descriptor:label:en:namespace
          - urn:alm:descriptor:label:zh:namespace(zh)
          - urn:alm:descriptor:com.tectonic.ui:select
          - urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/api/v1/namespaces
          - urn:alm:descriptor:expression:props.options:path:items
          - urn:alm:descriptor:expression:props.options:label:path:metadata.name
          - urn:alm:descriptor:expression:props.options:value:path:metadata.name
          - urn:alm:descriptor:com.tectonic.ui:validation:required
spec:
  params:
    - name: namespace
      type: string
  steps:
    - name: demo
      image: ubuntu
      script: |
        #!/bin/sh
        pwd

Configure Dynamic Forms in Pipeline

Example: Provide a dropdown box for selecting namespaces for the target-namespace parameter of a Pipeline.

Effect: When users trigger a Pipeline based on the UI or need to associate this pipeline in other resources (Trigger, TriggerTemplate) and configure runtime parameters, the target-namespace parameter supports dropdown selection of namespaces.

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  annotations:
    style.tekton.dev/descriptors: |
      - path: params.target-namespace
        x-descriptors:
          - urn:alm:descriptor:label:en:target-namespace
          - urn:alm:descriptor:label:zh:target-namespace(zh)
          - urn:alm:descriptor:com.tectonic.ui:select
          - urn:alm:descriptor:expression:props.options:api:/kubernetes/${context.cluster}/api/v1/namespaces
          - urn:alm:descriptor:expression:props.options:label:path:metadata.name
          - urn:alm:descriptor:expression:props.options:value:path:metadata.name
          - urn:alm:descriptor:expression:props.options:path:items
          - urn:alm:descriptor:com.tectonic.ui:validation:required
  name: demo-pipelines
  namespace: <your namespace>
spec:
  params:
    - name: target-namespace
      type: string
  tasks:
    - name: kubectl
      taskRef:
        resolver: hub
        params:
          - name: kind
            value: task
          - name: catalog
            value: catalog
          - name: name
            value: kubectl
          - name: version
            value: "0.1"

Troubleshooting

  • Form not displayed or configuration not taking effect: Check if the YAML format of style.tekton.dev/descriptors is correct, confirm path is configured as params.parameter-name, workspaces.workspace-name, or results.result-name as appropriate, and verify each descriptor starts with - urn:alm:descriptor:
  • Dynamic options not loading: Confirm the API path is correct, check if the current user has permission to access the specified API resource, verify path, label and value configurations point to fields that exist in the returned data
  • Default value not taking effect: Confirm fixed default value uses expression:props.default:value format, expression default value uses === instead of =, check if variables (option, index, length, context) in the expression are spelled correctly
  • Context variables not resolved: Verify variable names are correct (e.g., context.namespace not context.namespaces), confirm variables are used in descriptors that support expressions, check if JavaScript expression syntax has errors
  • Link not displayed: Confirm enhance:docLink uses either productDoc or external, verify docLink:link is present, and ensure external links use full http or https URLs
  • Field is displayed but cannot be edited: Check whether urn:alm:descriptor:com.tectonic.ui:disabled is configured for the field
  • Advanced control is not displayed: Check that the descriptor path matches an existing parameter, that the required widget capability is present, and that the current entry supports the configured control. Use the Basic form to verify the underlying value separately.
  • Array table does not render: Check that arrayTable:items contains at least one item with a unique, non-empty path. Invalid item definitions fall back to the regular array form.
  • Array value changes are not saved: Check the separator or the parse.exp and stringify.exp expressions. A failed conversion or invalid nested field does not overwrite the original array value.
  • Inline form is empty: Check that the selected resource, resource path, parameter path, and descriptor path resolve in the current context. Also verify that the current user can read the resource API.
  • Preview is different from the final run: Check whether the preview depends on runtime-only variables. Preview results are for reference when the execution context is not available.
  • Workspace or Result control is unchanged: Workspace and Result descriptors provide display metadata only. Required, optional, and binding behavior still comes from the Pipeline declaration.