Back to Activepieces

Property UI Selection Guide

.agents/skills/piece-builder/property-ui-selection.md

0.88.311.7 KB
Original Source

Property UI Selection Guide

Read this before defining props on any action or trigger. props-patterns.md tells you the syntax of each property type; this file tells you which component, display mode, and layout to pick for the use case so the step form reads well for a non-technical user.

The mental model: choose the input type → apply a display upgrade if one fits → arrange with layout hints → group with propertyGroups only when the form is large or has distinct modes. Most props need only the first step.


1. Pick the input component (by use case)

The user needs to enter…UseNotes
A short single-line value (name, email, id)Property.ShortTextAdd placeholder. Prefer a dropdown over a raw ID field — see rule below.
A long free-form value (notes, description)Property.LongTextMulti-line textarea.
A formatted message body (email, chat post)Property.RichText + formatPropertyToolbar + {{ variables }}. Pair with a sibling format dropdown. See §2.
Yes/no, on/offProperty.CheckboxUse reveals to show dependent fields only when on. See §2.
A numberProperty.NumberAdd display: 'stepper' for bounded counts. See §2.
One choice from a fixed listProperty.StaticDropdownAdd display: 'cards' for ≤4 visual choices. See §2.
Many choices from a fixed listProperty.StaticMultiSelectDropdown
One choice fetched from the APIProperty.Dropdownrefreshers, options async. This is the answer to "don't make users type IDs".
Many choices fetched from the APIProperty.MultiSelectDropdown
A date + timeProperty.DateTimeSingle instant.
A time window (last 7 days, custom range)Property.DateRangeFor search/filter actions. display: 'dropdown' inside a filter builder. See §2.
Fields that change based on earlier input/APIProperty.DynamicPropertiesBuild the sub-form in props(). Heaviest option — use only when the shape is genuinely runtime-dependent.
A file (upload or URL)Property.FileReturns ApFile.
A colorProperty.ColorSwatch + hex.
Raw JSONProperty.JsonOnly when there is no better structured option.
Free-form key→value pairsProperty.ObjectDictionary editor.
A list of plain stringsProperty.Array (no properties)Tags, emails.
A list of structured rowsProperty.Array with propertiesRepeating record editor (e.g. line items).
Read-only instructions / setup stepsProperty.MarkDownDisplay-only, collects nothing. Use variant (INFO/WARNING/TIP/BORDERLESS).
A fully custom widget (embedding only)Property.Custom (BETA)DOM injection. Requires minimumSupportedRelease >= 0.58.0. Avoid unless embedding.

Never make users type an opaque ID. If a value exists behind the API (channel, project, contact, board), use Property.Dropdown so they pick it by name. Raw ShortText for an ID is a last resort and needs a description that explains exactly where to find it.


2. Display upgrades (opt-in, per property)

These are optional display/pairing hints on an otherwise normal property. They are safe to add and ignored where they don't apply.

StaticDropdowndisplay: 'cards'

Use for a small set (2–4) of mutually exclusive modes where each choice benefits from an icon + one-line explanation (e.g. Plain text / HTML / Markdown). Do not use cards for long lists — they don't scroll well.

typescript
Property.StaticDropdown({
  displayName: 'Format', required: true, defaultValue: 'plain_text', display: 'cards',
  options: { options: [
    { label: 'Plain text', value: 'plain_text', description: 'Simple', icon: 'text' },
    { label: 'HTML', value: 'html', description: 'Rich + styled', icon: 'code' },
  ] },
});

Numberdisplay: 'stepper'

Use for a bounded count the user nudges (max results, retries, quantity). Requires sensible min/max; add step.

typescript
Property.Number({ displayName: 'Max results', required: false, defaultValue: 10, display: 'stepper', min: 1, max: 500, step: 1 });

RichText + formatProperty

Use for any message body the user composes. Pair it with a sibling StaticDropdown (ideally display: 'cards') and point formatProperty at that dropdown's name. The returned value is a plain string in the chosen format.

  • Sibling value mapping (by convention): plain_text / plain / text → plain · html → HTML · markdown / md → markdown · anything else → plain.

DateRange (+ display: 'dropdown')

Use for "limit results to a time window" on search/list actions. Omit display for pill buttons; set display: 'dropdown' when it lives inside a filter builder. Resolve in run():

typescript
import { dateRangeUtils } from '@activepieces/pieces-framework';
const { after, before } = dateRangeUtils.resolve(context.propsValue.date_range); // ISO strings or undefined

Checkboxreveals

Use to progressively disclose fields that only matter when the toggle is on. List the dependent prop names; they render indented beneath the toggle.

typescript
has_attachment: Property.Checkbox({ displayName: 'Has attachment', required: false, defaultValue: false, reveals: ['attachment_name'] }),
attachment_name: Property.ShortText({ displayName: 'Attachment name', required: false, placeholder: 'e.g. invoice.pdf' }),

3. Layout hints (any property)

These live directly on the property. They fine-tune placement without changing the input.

HintValueUse when
placeholderstringAny text input — show an example value ([email protected]).
width'half'Two short related fields should sit side-by-side (First / Last name). Only takes effect inside a section group.
iconicon nameGive a filter-builder row or section field a leading glyph. Must be a valid name — see §5.
advancedtrueTucks a secondary field into the collapsible Advanced section. Nothing collapses there unless you set it — advanced: false is the default and does nothing.

Advanced section rule: every prop renders in the main form by default, required or not. Opt a field out with advanced: true. Don't set it on a required prop — the Advanced section starts collapsed, so a mandatory field hidden there only surfaces as a validation error.


4. Grouping props with propertyGroups

Only add groups when the form is large or has distinct concerns. A short form (≤4 fields) needs none. Declare propertyGroups on the action/trigger; each group lists members by name:

typescript
propertyGroups: [{ key, display, label?, description?, icon?, props: ['fieldA', 'fieldB'] }]

(Threaded through both createAction and createTrigger.)

Pick the layout by intent:

IntentdisplayBehaviour
Mutually-exclusive modes of the same concept (To / Cc / Bcc; by-URL vs by-ID)'tabs'Segmented control; one tab's fields visible at a time.
Related fields as a titled card (a "Send to" card, a "Message" card)'section'Titled card; width: 'half' packs two-up. Keeps the Advanced section for props outside the cards — group members are always essential.
A search/filter action where users add only the filters they need'builder'Progressive "Add filter" picker; each builder group is a category. A filter row persists only when its value is set — give each filter a placeholder + icon.
A pinned control below a filter builder (result limit)'footer'Pins its prop (e.g. a stepper) under the builder list. Pair with 'builder' groups — like 'builder', it disables the Advanced section form-wide.

Rules:

  • Every prop named in a group must exist in props.
  • Only ungrouped props honour advanced: true — members of tabs and section groups are always essential; the flag is ignored on them. In a sectioned layout, checkbox reveals targets are forced essential too: they render inline under their toggle, never in Advanced.
  • One builder or footer group disables the Advanced section for the whole action/trigger — every prop is forced essential and advanced: true stops working form-wide. Don't combine a filter builder with Advanced props.
  • Give section and builder groups a label and icon so cards/categories read clearly.

5. Valid icon names

icon accepts only these keys (each maps to a Lucide icon). Any other string renders nothing:

text · code · markdown · reply · reply-all · users · user · send · type
file · paperclip · tag · inbox · calendar · trash · filter · sliders · blank

6. Worked examples

Send a chat message — sectioned cards + card dropdown + rich body:

typescript
propertyGroups: [
  { key: 'destination', display: 'section', label: 'Send to', icon: 'send', props: ['chat_id'] },
  { key: 'message',     display: 'section', label: 'Message',  icon: 'text', props: ['format', 'message'] },
],
props: {
  chat_id: Property.ShortText({ displayName: 'Chat Id', required: true, placeholder: '@channel or 123456789' }),
  format:  Property.StaticDropdown({ displayName: 'Format', required: false, display: 'cards', options: { options: [/* Markdown / HTML / Plain */] } }),
  message: Property.RichText({ displayName: 'Message', required: true, formatProperty: 'format' }),
  disable_notification: Property.Checkbox({ displayName: 'Disable notification', required: false, advanced: true }), // → Advanced (ungrouped + flagged)
},

Search emails — filter builder + footer stepper + date range:

typescript
propertyGroups: [
  { key: 'people', display: 'builder', label: 'People', icon: 'users',    props: ['from', 'to'] },
  { key: 'time',   display: 'builder', label: 'Time',   icon: 'calendar', props: ['date_range'] },
  { key: 'footer', display: 'footer',  props: ['max_results'] },
],
props: {
  from: Property.ShortText({ displayName: 'From', required: false, icon: 'user', placeholder: '[email protected]' }),
  to:   Property.ShortText({ displayName: 'To',   required: false, icon: 'send', placeholder: '[email protected]' }),
  date_range:  Property.DateRange({ displayName: 'Date', required: false, display: 'dropdown', icon: 'calendar' }),
  max_results: Property.Number({ displayName: 'Max results', required: false, defaultValue: 10, display: 'stepper', min: 1, max: 500 }),
},

Recipients — segmented tabs:

typescript
propertyGroups: [{ key: 'recipients', display: 'tabs', label: 'Recipients', props: ['to', 'cc', 'bcc'] }],
props: {
  to:  Property.Array({ displayName: 'To',  required: true }),
  cc:  Property.Array({ displayName: 'Cc',  required: false }),
  bcc: Property.Array({ displayName: 'Bcc', required: false }),
},

7. Anti-patterns

  • Raw ID ShortText where a Dropdown is possible. Pick-by-name beats copy-paste-an-id every time.
  • display: 'cards' on a long list. Cards are for 2–4 modes; use a plain dropdown otherwise.
  • propertyGroups on a 3-field form. Grouping is overhead — only add it for large or multi-mode forms.
  • Invalid icon name. Anything outside the §5 list silently renders nothing; verify before shipping.
  • Property.Json as an escape hatch. If the shape is known, model it with real props or an Array of fields.
  • Property.DynamicProperties for a static form. It's the heaviest widget; only use it when fields truly depend on runtime data.
  • advanced: true on a required prop. Advanced starts collapsed; a mandatory field hidden there only surfaces as a validation error.

Full type syntax and dynamic-dropdown/refresher mechanics: props-patterns.md. Rendered previews of every option: docs/build-pieces/piece-reference/properties.mdx.