Developers can specify options that customers configure when installing or previewing your app.

Options are very powerful; you can use them to allow customers to configure the appearance of your app, and even conditionally load resources.

You can test your install.json file anytime by loading your app in the App Creator.

Defining options

The install.json file uses the JSON Schema format to define options.

Here’s a set of examples declaring install options fields and values provided by the customer.

The customer updates this form while installing or previewing your app, and its values are available to your app in a JavaScript object titled INSTALL_OPTIONS.

This is a live example! Change the values in the form above to see how the INSTALL_OPTIONS object changes.
INSTALL_OPTIONS = {{options.options | json}}
Declaring options via install.json
{
  "options": {
    "properties": {
      "location": {
        "title": "Location",
        "description": "Where should the app appear on your site?",
        "type": "object",
        "format": "element",
        "default": {
          "selector": "body",
          "method": "prepend"
        },
        "order": 1
      },
      "slides": {
        "title": "Slides",
        "type": "array",
        "maxItems": 10,
        "order": 2,
        "items": {
          "type": "object",
          "title": "Slide",
          "properties": {
            "image": {
              "title": "Image",
              "type": "string",
              "order": 1,
              "format": "image"
            },
            "description": {
              "title": "Description",
              "description": "This text is used as the image’s title and alt text, important for SEO.",
              "default": "A wonderful image portraying a better future.",
              "order": 2,
              "type": "string",
              "maxLength": 200
            }
          }
        }
      }
    }
  }
}

You can find many more examples in the cloudflare-apps repositories.

Option type reference

Universal attributes

All field types accept the following attributes:

title

A text label to accompany the option input. (e.g. “Color”)

description

A longer description of the option. (e.g. “The color of the app.”)

default

The default value to be used for the option.

Keep in mind the default value must be of the type specified by the option and child options, if any. So for example, the default for an array of strings type would be ["Item one", "Item two"].

order

As JSON objects don’t necessarily maintain their order, you must specify the desired order of your fields using the order property. order can be any numerical value, and must appear on every field which has at least one sibling.

Primitive types

String

string

Presented as a text input

value = {{options.string.stringExample | json}}
{
  "title": "String Example",
  "type": "string",
  "default": "You can do anything you want to do. This is your world.",
  "placeholder": "It’s life. It’s interesting. It’s fun."
}
maxLength

String types accept a maxLength property which defines the maximum number of characters the field should allow.

placeholder

String types allow you to specify the placeholder value which will appear as lighter-than-normal text in the input when it is empty. This is a useful place to include an example of how a value to be placed in that field should look.

Many developers duplicate the placeholder content as a default text in their app. This allows customers to always have sane default labels that can be overridden for localization. If this proves cumbersome, we recommend importing the install.json contents into your app JavaScript with Webpack.

Demo this technique with Welcome Bar

URL String

string/url

Presented as a text input with validation to ensure the customer provides a URL. The default placeholder can be overridden.

value = {{options.urlString.urlStringExample | json}}
{
  "title": "URL String Example",
  "type": "string",
  "format": "url"
}

Email String

string/url

Presented as a text input with validation to ensure the customer provides an email address. The default placeholder can be overridden.

value = {{options.emailString.emailStringExample | json}}
{
  "title": "Email String Example",
  "type": "string",
  "format": "email"
}

Long string

string/textarea

Presented as an auto-expanding textarea, with spellcheck enabled (as supported by browser/OS).

value = {{options.textarea.textareaExample | json}}
{
  "title": "Textarea Example",
  "type": "string",
  "format": "textarea",
  "default": "When the Internet first came, I thought it was just the beacon of freedom. People could communicate with anyone, anywhere, and nobody could stop it.\n\n-Steve Wozniak"
}

Code

string/code

Presented as an auto-expanding textarea typeset with a monospaced font, with spellcheck disabled. (In the future, this field will be presented as a more fully-featured code editor.)

Submitted apps that use code fields to insert script tags will be rejected.
See our ease of use and security guidelines.

value = {{options.code.codeExample | json}}
{
  "title": "Code Example",
  "type": "string",
  "format": "code",
  "default": "body {\n  color: orange;\n}"
}

Richtext

string/richtext

Presented as a richtext editor, with spellcheck enabled (as supported by browser/OS).

value = {{options.richtext.richtextExample | json}}
{
  "title": "Richtext Example",
  "type": "string",
  "format": "richtext",
  "maxLength": 500,
  "default": "<h1>This is your world.</h1><p><br><strong>You’re the creator.</strong><br>Find freedom on this canvas.<br>Believe, that you can do it...</p><p>‘Cuz <em>you</em> can do it.<br><strong>You can do it.</strong></p><p>― Bob Ross</p>"
}

Markdown

object/markdown

Presented as an auto-expanding textarea, with spellcheck enabled (as supported by browser/OS).

The entered value should be written in Showdown-flavored Markdown and will be converted into HTML. Both the markdown and html are set as values on the resulting JavaScript object.

value = {{options.markdown.markdownExample | json}}
{
  "title": "Markdown Example",
  "type": "object",
  "format": "markdown",
  "default": "# Simply making decisions...\n*One after another*, can be a form of art.\n\n ― John Gruber"
}

This field is of type object as we provide you with both the original Markdown and the rendered HTML.

Image

string/image

Presented as an image uploader

value = {{options.image.imageExample | json}}
{
  "title": "Image Example",
  "type": "string",
  "format": "image"
}

The value is provided as a string URL pointing to the image hosted on Cloudflare’s content delivery network.

Color

string/color

Presented as a color picker and accompanying text input for manually specifying a hex value

value = {{options.color.colorExample | json}}
{
  "default": "#f38020",
  "format": "color",
  "title": "Color Example",
  "type": "string"
}

Limiting a color picker to a set of products*

Color pickers can be limited to specific products by declaring a products property. The value is an array containing a productId string for each eligible product.

In the example below, a customer has chosen the 'basic' plan and is limited to the default slider value. Only after upgrading to 'plus' and 'pro' can the customer choose a different value.

INSTALL_PRODUCT = {id: 'basic'}
{
  "title": "Custom Accent Color",
  "type": "string",
  "format": "color",
  "products": ["plus", "pro"],
  "default": "#0099ff"
}

The value is provided as a hex color code beginning with a #, for example #2F4A99.

Date

string/date

Presented to the customer with a date picker. The resulting date value is provided to the app in a format understood by new Date.

value = {{options.date.dateExample | json}}
{
  "title": "Date Example",
  "type": "string",
  "format": "date",
  "default": "2015-10-10"
}

Time

string/time

Presented to the customer as a dropdown of times, and a set of radio buttons to control timezone behavior.

The radio buttons are required because a specific time can mean two different things. “9:00 PM” could either mean the time “9 PM” in each website visitor’s local timezone, or it could mean “9 PM” in the users’s timezone.

For example, Saturday Night Live airs at “11:30 PM EST” irrespective of what timezone you are in (making it a single moment in time). For viewers in California that same moment in time would be represented as “8:30 PM PST”.

On the other hand, your “Midnight Madness Sale” might end at “12:00 AM” in the local timezone of the visitor (meaning it will end at midnight for every visitor). A viewer in New York would experience it at “12:00 AM EST”, a viewer in California at “12:00 AM PST”. This means that there would be no single moment when the sale ends for every visitor, rather it would end for everyone at various moments in a 24-hour period based on their timezone.

value = {{options.time.timeExample | json}}
{
  "title": "Time Example",
  "type": "string",
  "format": "time",
  "default": "9:30 PM"
}
Based on the value of the radio option, this value will be presented to you as either the time alone, or the time with a timezone offest. For example 21:30, or 21:30 +0500. The recommended way for you to consume this in your JavaScript is to add it to a real or dummy date.
date = new Date('Jan 1 2018 ' + options.time)
If your goal is to compare it to the current time, use the current time in your test:
var date = new Date((new Date).toDateString() + ' ' + options.time)
var isPast = date < new Date()

Date Time

string/date-time

Presented to the customer as a date picker and a dropdown of times.

The dropdown of times also includes the timezone radio buttons as described in the Time section above.

value = {{options.datetime.datetimeExample | json}}
{
  "title": "Date Time Example",
  "type": "string",
  "format": "date-time",
  "default": "March 17 2015 9:30 PM EST"
}
You can use the provided value in the JavaScript Date constructor.
var date = new Date(options.eventTime)

CSS selector of an existing element on the users’s website

string/selector

Presented as an interactive field which lets the customer visually select a location in their website and then specify a location on their website. For advanced customers, a text input is also provided to manually specify a CSS selector.

value = {{options.selector.selectorExample | json}}
{
  "title": "Selector Example",
  "type": "string",
  "format": "selector",
  "default": "body"
}

Once collected, the element the selector refers to can be loaded using document.querySelector or document.querySelectorAll. To ensure that your app does not error on pages which it was not originally installed on, you should confirm that the element is not null before using it:

var element = document.querySelector(INSTALL_OPTIONS.location);
if (!element) return;

element.textContent = "You’ve found me!";

See the element format below for an input type which is designed to allow the customer to place new elements, rather than just selecting an existing one.

Page selector on an users’s website

object/page

Presented as an interactive field which lets the customer pick pages on their website. This option will also automatically appear when using an Element Selector in an array.

Demo this technique with CSSgram

value = {{options.page.pageExample | json}}
{
"title": "Sets of images to filter",
"order": 1,
"type": "array",
"items": {
  "notitle": true,
  "type": "object",
  "properties": {
    "location": {
      "order": 1,
      "title": "Location",
      "description": "Select the part of your page which contains the images you’d like to filter.",
      "type": "string",
      "format": "selector"
    },
    "pages": {
      "order": 2,
      "title": "Pages",
      "description": "Which pages should use this filter?",
      "type": "object",
      "format": "page"
    },
    "filter": {
      "order": 3,
      "title": "Filter",
      "type": "string",
      "enum": [
        "aden",
        "brannan",
        "clarendon"
      ],
      "enumNames": {
        "aden": "Aden",
        "brannan": "Brannan",
        "clarendon": "Clarendon"
      },
      "default": "aden"
    }
  }
},
"default": [
  {
    "location": "body",
    "filter": "aden"
  }
]
}

It’s strongly recommended that you use INSTALL.matchPage to determine if the customer’s selection matches:

(function () {
'use strict'

var options = INSTALL_OPTIONS
var element

function updateElements () {
options.regions
.filter(function (region) { return INSTALL.matchPage(region.pages) })
.forEach(function (region) {
element = document.querySelector(region.selector)

      if (!element) return

      element.classList.add(region.filter)
    })

}
}())

DOM element to be added to the users’s website

object/element

Presented as an interactive field which lets the customer visually select a location in their website and then specify where an <cloudflare-app> element will be placed relative to that location. For advanced customers, a text input is also provided to manually specify a CSS selector.

value = {{options.element.elementExample | json}}
{
  "title": "Element Example",
  "type": "object",
  "format": "element"
}

method values

A page selector button will automatically be inserted in when the location field appears in an array. This will allow customers to insert specific elements on specific pages.

It’s strongly recommended that you use INSTALL.createElement to convert the value into an element in your app’s source code:

"use strict";

var options = INSTALL_OPTIONS;
var element;

function updateElement() {
  element = INSTALL.createElement(options.location, element);
  element.setAttribute("app", "example");

  // Use element however you like.
  element.textContent = "Veni, vidi, vici.";
}

INSTALL.createElement will automatically handle placing the new element in the correct location, as specified by method. For more information about createElement, and the optional Live Preview argument, see the Environment documentation.

An account for a third-party service

object/account

{
  "title": "Google Account",
  "type": "object",
  "format": "account",
  "services": ["googleanalytics"],
  "required": true
}

Using Services and Hooks, you can allow customers to OAuth into third-party services. For example you may wish for them to login to your service to include thier user ID in the embed code delivered by Cloudflare.

After defining a service, you can use the account field to allow the customer to login.

Floating-point number

number

Presented as a number-style text input

value = {{options.number.numberExample | json}}
{
  "default": 0.5,
  "title": "Number Example",
  "type": "number"
}

Integer number

integer

Presented as a number-style text input which only accepts an integer.

Both integers and floating-point numbers can set a minimum and maximum value.

value = {{options.integer.integerExample | json}}
{
  "title": "Integer Example",
  "description": "Input a number between 5 and 50.",
  "type": "integer",
  "default": 5,
  "minimum": 5,
  "maximum": 50
}

Number with unit

object

Presented as a floating-point number input and unit selector. This is useful when a customer has to set a specific size on an element.

{{ options.numberUnit.numberUnitExample | json}}
{
  "description": "How wide should the element be?",
  "title": "Width",
  "type": "object",
  "format": "number",
  "units": ["px", "em", "%"],
  "minimum": 0,
  "default": {
    "value": 50,
    "unit": "%"
  }
}

Number fields can be difficult for non-techincal customers. Consider a set of default radio inputs, or a slider.
Read more on ease of use

Slider

number/slider, integer/slider

Presented as a draggable slider with optional step amount and range restrictions

value = {{options.slider.sliderExample | json}}
{
  "title": "Number Slider Example",
  "type": "number",
  "format": "slider",
  "default": 50,
  "minimum": -7,
  "maximum": 70,
  "step": 3.5
}

Limiting a slider to a set of products*

Slider options can be limited to specific products by declaring a products property. The value is an array containing a productId string for each eligible product.

In the example below, a customer has chosen the 'basic' plan and is limited to the default slider value. Only after upgrading to 'plus' and 'pro' can the customer choose a different value.

INSTALL_PRODUCT = {id: 'basic'}
{
  "title": "Exclusive Slider",
  "type": "number",
  "format": "slider",
  "products": ["plus", "pro"],
  "default": 10,
  "minimum": -5,
  "maximum": 50,
  "step": 5
}

True or false value

boolean

Presented as a checkbox

value = {{options.boolean.booleanExample | json}}
{
  "title": "Boolean Example",
  "type": "boolean"
}

Limiting a boolean checkbox to a set of products*

Boolean options can be limited to specific products by declaring a products property. The value is an array containing a productId string for each eligible product.

In the example below, a customer has chosen the 'basic' plan and is limited to the default slider value. Only after upgrading to 'plus' and 'pro' can the customer choose a different value.

INSTALL_PRODUCT = {id: 'basic'}
{
  "title": "Disable Branding",
  "type": "boolean",
  "products": ["plus", "pro"],
  "default": false
}

A list of values to select from

string with enum

Presented as a <select> element with enum strings as <option>s. Specify enumNames for human-readable names

value = {{options.enum.enumExample | json}}
{
  "default": "option-a",
  "title": "Enum Example",
  "type": "string",
  "enum": [
    "option-a",
    "option-b"
  ],
  "enumNames": {
    "option-a": "Option A",
    "option-b": "Option B"
  }
}

Radio buttons to select a value from

string/radios with enum

You can specify that your enum field be shown as radio buttons, rather than a select dropdown:

value = {{options.radios.radiosExample | json}}
{
  "default": "option-a",
  "format": "radios",
  "title": "Radios Example",
  "type": "string",
  "enum": [
    "option-a",
    "option-b"
  ],
  "enumNames": {
    "option-a": "Option A",
    "option-b": "Option B"
  }
}

Limiting radio options to a set of products*

Radio options can be limited to specific products with products. Much like enumNames, each key in the object is a string from the enum array. However the value is an array containing a string productId for each eligible product.

In the example below, a customer has chosen the 'basic' plan and can only choose the first option. The other choices are limited to 'plus' and 'pro' respectively. enum entries that are not specified are choosable regardless of product choice.

INSTALL_PRODUCT = {id: 'basic'}
{
  "title": "What’s the goal you’re trying to achieve?",
  "type": "string",
  "format": "radios",
  "enum": [
    "announcement",
    "cta",
    "signup"
  ],
  "enumNames": {
    "announcement": "Just show a message",
    "cta": "Redirect them to a special page",
    "signup": "Gather emails to sign visitors up for your newsletter"
  },
  "products": {
    "cta": ["plus", "pro"],
    "signup": ["pro"]
  },
  "default": "announcement"
}

Help

help

Presented as a rendered Angular-sanitized HTML.

This field provides an opportunity to provide extra instructions or context to the install process. Unlike every other option type, it doesn’t collect any input nor set any value on the INSTALL_OPTIONS object.

This should be used sparingly. If you believe your install option needs more context, consider separating the installation flow in to more steps, or move this content into the app configuration page “Additional Install Instructions” field.

Here you can add emphasis and other HTML markup like a[href], strong, and code.
{
  "type": "help",
  "helpvalue": "Here you can add <em>emphasis</em> and other HTML markup like <code>a[href]</code>, <code>strong</code>, and <code>code</code>."
}

Container types

Container types allow you to group other fields into objects or arrays.

Nested set of options

object

Presented inside of a container with its own optional label

{
  "type": "object",
  "properties": {…}
}

The properties block is in turn defined as a mapping between keys and other options blocks:

INSTALL_OPTIONS = {{options.object | json}}
{
  "properties": {
    "color": {
      "default": "#000000",
      "format": "color",
      "title": "Color",
      "type": "string"
    },
    "size": {
      "default": 0,
      "title": "Size",
      "type": "number"
    }
  }
}

Those properties would then be available as the .color and .size properties inside the objects value within INSTALL_OPTIONS.

List of options

array

Presented as a list of a single option type (specified inside directly inside of items), with the ability for the customer to add and remove items.

{
  "type": "array",
  "items": {
    [ Any primitive or container option definition ]
  }
}

For example:

value = {{options.array.arrayExample | json}}
{
  "title": "Array Example",
  "type": "array",
  "items": {
    "title": "Item",
    "type": "object",
    "properties": {
      "text": {
        "order": 1,
        "title": "Text",
        "type": "string"
      },
      "size": {
        "order": 2,
        "title": "Size",
        "type": "number"
      }
    }
  }
}

The value is then available as an array of values, each of the type defined in items.

maxItems

The maxItems attribute allows you to specify a maximum number of items the user should be allowed to specify.

Using option values

User-selected options can be used in your app’s JavaScript.

JavaScript

The install option values are available using the INSTALL_OPTIONS object. You can use INSTALL_OPTIONS in files or in the contents block in your install.json file.

For example, if you define your options as:

"options": {
  "properties": {
    "color": {
      "title": "Color",
      "type": "string",
      "format": "color"
    }
  }
}

Your INSTALL_OPTIONS object will include INSTALL_OPTIONS.color.

Working with existing projects

If you would like your project to be usable both by developers directly, and using Cloudflare, simply pass the INSTALL_OPTIONS into your tool’s initialization function:

"resources": {
  "head": [
    {
      "type": "script",
      "src": "./social-icons.js"
    },
    {
      "type": "script",
      "contents": "SocialIcons.init(INSTALL_OPTIONS)"
    }
  ]
}

If you need to convert or modify the options, you can add a helper file to your project, commonly located at install/app.js. It’s also common to do any work necessary to turn a general-purpose library into something which can be simply used by non-technical people in this file. For example, look at the CSSGram install helper which interprets options for the CSSGram App and allows it to work on websites which don’t have the special markup it requires.

Files

You can use install options to control which files are included in your app. You can do this by including variables in the file’s src in the resources section of the install.json file:

"src": "./themes/{{options.color}}/pace-theme-{{options.theme}}.css"

Dynamic fields

Cloudflare options allow you to define when fields should be shown to the customer based on the value of other fields using the showIf attribute.

For example, to only show the breed field when animal is "dog":

INSTALL_OPTIONS = {{options.showIf | json}}
{
  "properties": {
    "animal": {
      "order": 0,
      "title": "Animal",
      "type": "string",
      "format": "radios",
      "enum": ["cat", "dog", "horse"],
      "enumNames": {
        "cat": "Cat",
        "dog": "Dog",
        "horse": "Horse"
      },
      "default": "cat"
    },
    "breed": {
      "order": 1,
      "title": "Breed",
      "type": "string",
      "showIf": {
        "animal": "dog"
      }
    }
  }
}

The showIf field can accept multiple criteria, all of which must be matched for the field to be visible. It also supports additional test methods:

INSTALL_OPTIONS = {{options.showIfOperator | json}}
{
  "properties": {
    "animal": {
      "description": "Fill this field to show breed.",
      "title": "Animal",
      "type": "string"
    },
    "breed": {
      "title": "Breed",
      "type": "string",
      "showIf": {
        "animal": {
          "op": "!=",
          "value": ""
        }
      }
    }
  }
}

The full list of supported ops are:

You can match fields nested within objects using the dot syntax:

"showIf": {
 "account.service": {
    "op": "!=",
    "value": null
  }
}

Arrays can be used as values as well:

INSTALL_OPTIONS = {{options.showIfOperatorArrayValue | json}}
{
  "properties": {
    "kind": {
      "order": 1,
      "title": "Kind",
      "description": "Select \"Cat\" or \"Dog\" to reveal Breed field.",
      "type": "string",
      "default": "sheep",
      "enum": ["cat", "dog", "sheep"],
      "enumNames": {
        "cat": "Cat",
        "dog": "Dog",
        "sheep": "Sheep"
      }
    },
    "breed": {
      "order": 2,
      "title": "Breed",
      "type": "string",
      "showIf": {
        "kind": {
          "method": "any",
          "op": "==",
          "value": ["cat", "dog"]
        }
      }
    }
  }
}

The full list of supported methods are:

Array items can be matched using the bracket syntax:

INSTALL_OPTIONS = {{options.showIfOperatorArray | json}}
{
  "properties": {
    "animals": {
      "title": "Animals",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "kind": {
            "title": "Kind",
            "description": "Select \"Dog\" to reveal Breed field.",
            "type": "string",
            "enum": ["cat", "dog", "horse"],
            "enumNames": {
              "cat": "Cat",
              "dog": "Dog",
              "horse": "Horse"
            },
            "default": "cat"
          },
          "breed": {
            "title": "Breed",
            "type": "string",
            "showIf": {
              "animals[].kind": "dog"
            }
          }
        }
      }
    }
  }
}

Showing advanced options

It’s common to use showIf to hide advanced options behind a toggle:

INSTALL_OPTIONS = {{options.showAdvanced | json}}
{
  "showAdvanced": {
    "order": 1,
    "type": "boolean",
    "title": "Show advanced options",
    "default": false
  },
  "advanced": {
    "showIf": {
      "showAdvanced": true
    },
    "order": 2,
    "type": "object",
    "title": "Advanced options",
    "properties": {
      "urlType": {
        "default": "automatic",
        "order": 1,
        "type": "string",
        "title": "URL to Like",
        "format": "radios",
        "enum": [
          "automatic",
          "provided"
        ],
        "enumNames": {
          "automatic": "Automatically use the URL of the page displaying the button",
          "provided": "Specify a particular URL"
        }
      }
    }
  }
}

Showing options with products*

You can also use showIf to show options based on the current product the customer has elected to purchase:

If this were a paid app, the field would be visible when the 'business' plan was selected.

{
  "properties": {
    "animal": {
      "title": "Animal",
      "type": "string",
      "showIf": {
        "INSTALL_PRODUCT.id": "business"
      }
    }
  }
}

* We are improving the developer experience of making your app paid on Cloudflare. If you'd like you're app to be paid, we recommend having customers pay through your service using hooks to reflect the product in the app.