Skip to main content

Custom plugin form builder

Create user-facing configuration fields with YAML.

Ryan avatar
Written by Ryan
Updated over a week ago

If you're new to building plugins, see Private Plugins and Public Plugins API Docs.

Below is a guide to adding user-facing form fields inside a Private Plugin or Recipe.

How it works

Step 1 - Plugin author (you) creates custom fields

Step 2 - Plugin user (including you) can interact with rendered fields

Step 3 - Provided values are accessible inside the Markup Editor

{{ trmnl.plugin_settings.custom_fields_values }}

# => { "password": "secret", "lookback_period": "7" }

These values are also accessible from the settings themselves, for example this field:

- keyname: api_key
field_type: string
name: API Key
description: Find this at my-server.com/settings


Can be leveraged in a Polling Header or Polling URL with just the keyname:

https://my-server.com/api/stats?access_token={{ api_key }}

For real-life examples, fork a few published Recipes:
https://usetrmnl.com/recipes

Supported field types

Where possible, TRMNL follows HTML5 input types to leverage native browser validations and UX features. Our current selection includes:

  • author_bio

  • url

  • string

  • text (textarea)

  • code (like textarea, but monospace font)

  • number

  • password

  • date (M Y D)

  • time (HH:SS)

  • select (single or multi)

  • time_zone

  • copyable (produces 'click to copy' button)

Coming soon:

  • radio (binary toggle)

  • email

  • xhrFetch (select field that fetches <option> values from external URL)

Anatomy of a form field

The following key/value pairs are available to all field types:

  • keyname - parameterized identifier, not user-facing, ex user_email

  • field_type - one of the above "type" values

  • name - field label

  • description - secondary field label, HTML-friendly

  • help_text - tertiary field label, HTML-friendly (optional)

  • optional - boolean; unless set to true, required will be added to HTML input (omit if field should be required)

  • default - value if input is left blank; must be a lowercase, parameterized version of one of the provided options values or will not be pre-chosen on select field type. example: "Upcoming Movies" option => upcoming_movies as default

The following key/value pairs are available for some field types:

  • rows - number of visible rows on a text or code field

  • options - collection of option values for the select field

  • placeholder - example value if input is cleared; supported by url, string, text, number, date, code

When building a form field, only the following values are required:

  • keyname, name, field_type

Building form fields

Below are examples of every supported field type, which you may modify with optional parameters described above.

Note that every field must be indented 2 spaces and be preceded with a -, also called a block sequence.

author_bio

this field type has specialized properties such as github_url, which produce clickable icons on your plugin, versus plain text links.

- keyname: doesnt_matter
name: About This Plugin
field_type: author_bio
description: Dad Jokes Daily™ was created by Abraham, father to many.
github_url: https://github.com/father-abraham
learn_more_url: https://www.bible.com/
email_address: fatherabe@hotmail.net

url

- keyname: url
field_type: url
name: Web Address
description: URL of RSS Feed
placeholder: https://www.site.com/rss
help_text: Please include http:// or https://

string

- keyname: api_key
field_type: string
name: API Key
description: Account API Key
placeholder: sa_api_key_qwerty

text (textarea)

- keyname: prompt
field_type: text
name: Prompt
description: Enter your prompt for Chat GPT to respond.
placeholder: Tell me a Dad Joke under 200 words.

number

- keyname: retirement_age
field_type: number
placeholder: '65'
name: Retirement Age
description: At what age do you plan to retire?

password

- keyname: password
field_type: password
name: Password
description: Mobile app login password
placeholder: s3cret!

code

- keyname: json_query
field_type: code
rows: 8
name: JSON Query
description: Provide a raw query
placeholder: |
{ "cannot": "spare a square" }
help_text: Learn how to do this <a href="https://jquery.com/" class="underline">here</a>.

date

- keyname: start_date
field_type: date
placeholder: '2024-11-26'
name: Start Date
description: Provide your start date

When using the date field, rendered form will have a M/D/Y picker. However the value inside {{ trmnl.plugin_settings.custom_fields_values }} will be YYYY-MM-DD.

time

- keyname: scroll_time
field_type: time
name: Fixed start time?
description: By default, the week's earliest event time will be used.
help_text: Optional. Applies to week view only.

select (single, unified label/value option tags)

- keyname: filter_by
field_type: select
options:
- Upcoming # value will be 'upcoming'
- Now Playing # value will be 'now_playing'
default: now_playing
name: Filter By
description: Select a movie type


select (single, with separate label/value option tags)

- keyname: lookback_period
field_type: select
options:
- One Week: 7
- Two weeks: 14
default: 14 # should be set to the value, not the label
name: Lookback Period

select (multi)

- keyname: sales_pipeline
field_type: select
name: Pipeline Statuses
description: Select up to 3 statuses
options:
- 'Discovery'
- 'Pending'
- 'Closed-lost'
- 'Closed-won'
multiple: true

Note: if you create a select field that has options "Yes" and "No", and you want the default option to be "No," then you must wrap the default in quotes as "no" or it will be considered truthy. Example:

- keyname: include_description
field_type: select
name: Include Description
description: Select up to 3 statuses
options:
- "Yes"
- "No"
default: "no" # unless wrapped in quotes, value will be true

time_zone

- keyname: time_zone
field_type: time_zone
name: Time Zone
description: Where are you located?

copyable

- keyname: webhook_url
field_type: copyable
name: Webhook URL
value: "https://vandelayindustries.com/hooks/IMPORTS_EXPORTS"
description: "(Webhook strategy only) Paste this into your integration"

Conditional field validations

As described above, all fields are required unless optional: true is included in the field's YAML definition.

But there may be cases where Field B should only be required if Field A has a given value, or vice versa. For this scenario we support conditional validations.

Simply attach conditional_validation to the (parent) field whose value determines which child field is required.

Here's an example with the native Weather plugin, which has 2x data providers (WeatherAPI, Tempest) and 2x user origin fields. If a user selects the Tempest data provider, only lat_long is required. But if a user selects the WeatherAPI data provider, the generic location field is required.

  - keyname: data_provider
field_type: select
options:
- Tempest
- WeatherAPI
default: tempest
name: Data Provider
description: Provides underlying metrics and layout design.
conditional_validation:
- when: "tempest"
required: ["lat_lon"]
- when: "weatherapi"
required: ["location"]
- keyname: lat_lon
field_type: string
name: Latitude/Longitude (Tempest only)
placeholder: -28.001499,153.428467
description: Provide your location.
optional: true
- keyname: location
field_type: string
name: Location (WeatherAPI only)
placeholder: Atlanta, GA, USA
description: Choose a location

This treatment produces the following user experience:

Troubleshooting

Form fields don't appear

If your YAML cannot be parsed for any reason, it will be ignored and no errors will be returned.

Check your YAML syntax with a free online tool to ensure proper formatting before saving it in the Custom Fields code editor:

Did this answer your question?