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.
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
.
INSTALL_OPTIONS
object changes.
INSTALL_OPTIONS = {{options.options | json}}
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.
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 string
s 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.
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.
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"
}
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"
}
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"
}
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.)
value = {{options.code.codeExample | json}}
{
"title": "Code Example",
"type": "string",
"format": "code",
"default": "body {\n color: orange;\n}"
}
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>"
}
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.
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.
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"
}
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
.
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"
}
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"
}
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)
var date = new Date((new Date).toDateString() + ' ' + options.time)
var isPast = date < new Date()
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"
}
Date
constructor.
var date = new Date(options.eventTime)
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.
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.
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)
})
}
}())
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
valuesbefore
: Before the elementafter
: After the elementprepend
: Inside the element before other siblingsappend
: Inside the element after other siblingsreplace
: Replace the element with a new elementIt’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.
{
"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.
Presented as a number-style text input
value = {{options.number.numberExample | json}}
{
"default": 0.5,
"title": "Number Example",
"type": "number"
}
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
}
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": "%"
}
}
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
}
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
}
Presented as a checkbox
value = {{options.boolean.booleanExample | json}}
{
"title": "Boolean Example",
"type": "boolean"
}
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
}
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"
}
}
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"
}
}
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"
}
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.
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 allow you to group other fields into objects or arrays.
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
.
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.
User-selected options can be used in your app’s 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
.
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.
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"
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 op
s 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 method
s are:
any
: at least one entry matches the criteria.all
: all entries match the criteria.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"
}
}
}
}
}
}
}
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"
}
}
}
}
}
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"
}
}
}
}