Plugin Library

Extend QuikForms with ready-to-use plugins. Each plugin downloads as a zip you deploy to your org, and each shows a different part of the plugin framework: JavaScript hooks, custom field types, Apex validators, submission handlers, and callout handlers.

Ctrl+K

Plugin Library Professional

The plugin framework requires the Professional plan. On Standard, plugins are not loaded: an installed plugin will not run and will not report an error. Everything else on this page, including the example source, is worth reading either way.

The QuikForms Plugin Library is a set of ready-to-use plugins that extend your forms. Each plugin is built on the QuikForms Plugin Framework and uses a different part of it: JavaScript lifecycle hooks, custom field types, Apex validators, submission handlers, and callout handlers. An active plugin loads on every form in your org.

The table below summarizes which framework features each plugin uses and where it runs:

Plugin JS Hooks Custom Field Type Apex Validator Apex Submission Handler Apex Callout Handler Error Behavior Experience Cloud Salesforce Sites
Input Masking Yes No No No No Not set Yes Yes
Range Slider No Yes No No No Not set Yes Yes
Smart Validation Yes No Yes No No Fatal Yes Yes
Submission Enrichment No No No Yes No Warn Yes Yes
External Verification Yes No No No Yes Skippable Yes Yes
Progress Indicator Yes No No No No Not set Yes Yes

All six plugins work on forms hosted on Experience Cloud and on Salesforce Sites. Where a detail differs between the two, the plugin's section says so. Error Behavior applies only to a plugin's Apex, so the three browser-only plugins leave it blank.

Installing a Plugin

Each plugin downloads as a zip of Salesforce metadata. It is not a managed package, so someone with permission to deploy metadata to your org installs it:

  1. Download the zip from the plugin's section below and unzip it. It contains up to three folders: staticresources (the browser code), customMetadata (the records that register the plugin and its hooks), and, for a plugin with a server half, classes (the Apex class and its test class).
  2. Deploy every folder in the zip, together, in one deployment. With the Salesforce CLI, copy the folders into the force-app/main/default directory of a Salesforce DX project and run sf project deploy start. Any deployment tool that accepts Salesforce DX source format works as well. The Custom Metadata records refer to the static resource and the Apex class, so deploying only some of the folders fails or leaves the plugin unregistered.
  3. Deploying Apex to a production org runs Apex tests, so every plugin that has Apex includes its test class. Deploy it with the class.
  4. Set the plugin's configuration (see Configuring a Plugin) and check that Is Active is selected on its record.

The files are already written for an org with QuikForms installed. The Custom Metadata records are named quikforms__QuikForms_Plugin.<Name> and their fields carry the quikforms__ prefix, and the Apex refers to QuikForms types by their namespace, such as quikforms.IQuikFormsPlugin. Deploy the files unchanged. The Plugin Framework Overview explains what each part does.

Configuring a Plugin

A plugin's settings are a JSON object in the Configuration Schema field (Configuration_Schema__c) of its QuikForms Plugin custom metadata record. To edit it, go to Setup → Custom Metadata Types, click Manage Records next to QuikForms Plugin, open the plugin's record (for example Input_Masking) and edit Configuration Schema.

Put the plugin's keys at the top level of that object. There is no wrapper object named after the plugin. For Input Masking, the whole field reads:

{
  "fields": {
    "phone": { "mask": "us-phone" }
  }
}

A form's browser code receives the object as formData.pluginConfig.<DeveloperName> (Input Masking reads formData.pluginConfig.Input_Masking), and the plugin's Apex receives it in initialize(). JSON that does not parse is ignored for that plugin alone, with a console warning in the browser, so a typo cannot break a form.

Every visitor's browser receives the object, except a top-level server object, which only the plugin's Apex receives. Put settings only the Apex needs, such as a callout's endpoint, inside server. Keep secrets in a Named Credential, never in this field.

Many settings name form fields. Unless a section says otherwise, a field is named by its Field ID, shown on the field in QuikField Manager and on the Basic Info tab of the field editor.

Input Masking

Input Masking Plugin

JS Hooks

Input Masking applies format masks to text fields as visitors type. It supports phone numbers, SSN, EIN, credit cards, postal codes, dates, and fully custom patterns. The visitor sees the formatted value, and the formatting characters are stripped from what is submitted, so Salesforce stores only the characters the visitor typed.

Download Plugin

Features

  • Real-time formatting. Characters are formatted as the visitor types, and the mask's literal characters, such as parentheses and hyphens, are inserted for them.
  • Invalid character prevention. A character that does not fit the mask position, such as a letter in a phone number slot, is not accepted.
  • Paste handling. Pasted text is stripped of its formatting and run through the mask.
  • Completeness check. On submit, a masked field that has a value but not every position filled blocks the submission with a message such as "Phone is incomplete. Expected format: (000) 000-0000 (7 of 10 characters entered)".
  • Clean data. When the mask is complete, the value submitted is only the characters the visitor typed: (555) 123-4567 is stored as 5551234567. The field on the page keeps showing the formatted value.
  • Predefined masks. Thirteen common masks cover US phone, SSN, EIN, credit card, postal codes, dates, times, currency, and percentage.
  • Custom patterns. Define your own masks using #, A, and * placeholders and literal characters.
Choose masks that suit the target field. Because the literals are stripped, a date typed as 12/31/2025 is submitted as 12312025, which a Salesforce Date field does not accept. Use the date and time masks on text fields.

Predefined Masks

The following predefined masks are available out of the box. Use the Name value in your configuration to reference them.

Name Pattern Example Output
us-phone (###) ###-#### (555) 123-4567
ssn ###-##-#### 123-45-6789
ein ##-####### 12-3456789
credit-card #### #### #### #### 4111 1111 1111 1111
us-zip ##### 90210
us-zip-plus4 #####-#### 90210-1234
ca-postal A#A #A# K1A 0B1
date ##/##/#### 12/31/2025
date-iso ####-##-## 2025-12-31
time ##:## 14:30
time-seconds ##:##:## 14:30:00
currency-us $#,###.## $1,234.56
percentage ###.##% 99.50%

Custom Mask Syntax

You can define your own masks using these placeholder characters:

Character Matches
# Any digit (0–9)
A Any letter (a–z, A–Z)
* Any letter or digit
Any other character A literal, inserted automatically as the visitor types

Configuration

Input Masking can take its list of masked fields from three places. When two of them name the same field, the one higher in this list wins:

  1. A data attribute on the field's input
  2. The plugin record (recommended)
  3. A global config object set by page code

The plugin record (recommended)

Put the JSON in the Configuration Schema field of the plugin's Input_Masking record (see Configuring a Plugin). It is the only one of the three that an admin can set without code, and it works on both Experience Cloud and Salesforce Sites:

{
  "fields": {
    "phone":        { "mask": "us-phone", "label": "Phone" },
    "ssn":          { "mask": "ssn",      "label": "Social Security Number" },
    "licensePlate": { "mask": "AAA-####", "label": "License Plate" }
  }
}

Each key under fields is a Field ID. An entry takes mask (a predefined name or a pattern) and an optional label used in the incomplete-value message, which otherwise uses the field's own label. A plain string also works: "phone": "us-phone". A mask attaches only to a single-line text input, and it limits the input to the mask's length.

The placeholder is generated from the mask for you: # becomes 0 and A or * becomes _, so us-phone shows (000) 000-0000.

An entry also accepts a required flag, but the plugin does not enforce it. To make a masked field required, turn on Required Field in the field editor, as for any other field.

Data attributes

Page code can set data-mask, and optionally data-mask-label, on a field's input by the time the form finishes loading. The plugin looks for these in the page document, so they apply to Salesforce Sites forms only. On Experience Cloud each field sits in a shadow root that the page document cannot see into.

// On a Salesforce Sites form a field's input has the id 'field-' + its Field ID
var phone = document.getElementById('field-phone');
phone.setAttribute('data-mask', 'us-phone');            // predefined mask
phone.setAttribute('data-mask-label', 'Phone');

document.getElementById('field-serial')
  .setAttribute('data-mask', 'AA-####-**');              // custom mask

A global config object

Page code, such as another plugin's script, can set window.QuikFormsInputMaskingConfig before the form loads, using the same fields shape. Use it when the mask depends on something only the page knows.

Public API

The Input Masking plugin exposes a public API on the QuikFormsInputMasking global object for programmatic access:

// Apply a mask to a raw value
QuikFormsInputMasking.applyMask('5551234567', 'us-phone');
// Returns: "(555) 123-4567"

// Strip mask formatting from a value
QuikFormsInputMasking.stripMask('(555) 123-4567', 'us-phone');
// Returns: "5551234567"

// Check if a masked value is complete (all positions filled)
QuikFormsInputMasking.isMaskComplete('(555) 123-____', 'us-phone');
// Returns: false

// Resolve a mask name to its pattern string
QuikFormsInputMasking.resolveMask('ssn');
// Returns: "###-##-####"

// Attach a mask. Both take a QuikForms FIELD ID, not a DOM element:
// the plugin finds the input through QuikFormsPlugins.getFieldElement.
QuikFormsInputMasking.attachMask('phone', 'us-phone', { label: 'Phone' });

// Remove a mask from that field
QuikFormsInputMasking.detachMask('phone');

Lifecycle Hooks

The Input Masking plugin registers the following hooks with the QuikForms plugin framework:

Hook What It Does
onFormLoad Reads the configuration, attaches the mask to each configured field (keystroke and paste handling, placeholder, maximum length), and formats any value already in the field
onFieldChange Re-applies the mask whenever the field's value changes, including a change made by another plugin or a dependency rule
onFieldBlur Re-applies the mask when the visitor leaves the field and updates the incomplete-value styling
onValidate Blocks the submit for any masked field whose value is incomplete, and returns the stripped value of every complete one as modifiedValues, which is what QuikForms submits

Range Slider

Range Slider Plugin

JS Field Type

Range Slider renders an interactive range slider with a floating value bubble, configurable bounds, optional tick marks, and your form's theme colors. It is a custom field type: an admin chooses it for a field in the field editor, and the field submits the number the visitor selects.

Download Plugin

Features

  • Configurable bounds. Set the minimum, maximum, step, and unit label for any numeric range.
  • Floating value bubble. A bubble follows the slider thumb while the visitor drags it, showing the current value and unit. The value is also shown below the slider at all times, with the minimum and maximum at either end.
  • Optional tick marks. Labeled reference marks can appear at an interval you choose.
  • Color-fill track. The filled part of the track, the thumb, and the bubble use your form's highlight color (--qf-highlight-color).
  • Touch and keyboard friendly. The slider is built on a native range input, so touch, arrow keys, and screen readers work, with ARIA values that include the unit.
  • Theme integration. The slider also uses the form's input background, input border, and text colors and its small corner radius, through the QuikForms CSS variables.

Configuration

After the plugin is deployed and active, set a field to use it:

  1. Open the field in the field editor, from QuikField Manager or from the field's edit button in the form builder.
  2. On the Options & Structure tab, set Plugin Field Type to Range Slider (Interactive). The list shows field types from active plugins, on the Professional plan.
  3. Plugin Configuration (JSON) fills in with the slider's defaults. Change them using the properties below. The editor checks that the JSON is valid before it saves.
  4. Save the field.

The field's own field type is kept and is used wherever the plugin is not active. If the plugin is deactivated, the picker shows the setting as rangeSlider (plugin not active) so it is not lost when the field is saved.

The slider always holds a value, starting at defaultValue, so a required slider field is always answered. A field set up before Plugin Configuration existed can hold the same JSON in its Description / Help Text instead. Plugin Configuration is read first, and it is the better place: a description is translated on a multi-language form, while Plugin Configuration is delivered exactly as stored.

Property Type Default Description
min number 0 Minimum slider value
max number 100 Maximum slider value
step number 1 Step increment between values
unit string "" Unit label displayed after the value (e.g., "%", "pts", "$")
defaultValue number 50 Initial value when the form loads
showTicks boolean false Whether to display tick marks along the track
tickInterval number 10 Interval between tick marks (only applies when showTicks is true)

Examples

Each example is the complete contents of a field's Plugin Configuration (JSON).

NPS Score (0–10)

{
  "min": 0,
  "max": 10,
  "step": 1,
  "unit": "pts",
  "defaultValue": 5,
  "showTicks": true,
  "tickInterval": 1
}

Budget Selector ($0–$100,000)

{
  "min": 0,
  "max": 100000,
  "step": 5000,
  "unit": "$",
  "defaultValue": 25000,
  "showTicks": true,
  "tickInterval": 25000
}

Priority Level (1–5)

{
  "min": 1,
  "max": 5,
  "step": 1,
  "unit": "",
  "defaultValue": 3,
  "showTicks": true,
  "tickInterval": 1
}

Satisfaction Percentage (0–100%)

{
  "min": 0,
  "max": 100,
  "step": 5,
  "unit": "%",
  "defaultValue": 50,
  "showTicks": true,
  "tickInterval": 25
}
Field Type Registration: The Range Slider registers a custom field type named rangeSlider, and its QuikForms Custom Field Type record (Range_Slider, deployed from the zip) is what lists it in the Plugin Field Type picker as Range Slider. See Custom Field Types to build your own.

Smart Validation

Smart Validation Plugin

JS Apex Hooks Validator

Smart Validation checks values in the browser as the visitor fills in the form (email typo suggestions, phone digit counts, character counters, and cross-field rules), and an Apex validator checks email, phone, and custom regex rules again on the server, where turning off JavaScript cannot skip them.

Download Plugin

Features

  • Email typo suggestions. When the visitor leaves an email field whose domain is one or two letters away from a common provider (for example gmial.com), the plugin shows "Did you mean ...@gmail.com?". Clicking the suggestion applies it. The providers are gmail.com, yahoo.com, hotmail.com, outlook.com, aol.com, icloud.com, mail.com, protonmail.com, live.com, and msn.com.
  • Email and phone checks. An email field must hold a well-formed address, and a phone field must contain at least 10 digits once formatting is ignored. Both are checked in the browser and again on the server.
  • Character counters. A live "120 / 500 characters" count appears under a field, turning amber as it nears the limit and red at the limit. A value over the limit blocks the submit.
  • Inline messages. Messages appear under the field when the visitor leaves it and clear as soon as they change it.
  • Cross-field rules. Rules enforce relationships between fields: matching values, date ordering, and conditional requirements.
  • Server-side regex rules. The Apex validator applies regex patterns you configure, on the server.

Which fields the email and phone checks cover

The checks apply to every matching field on every form in your org as soon as the plugin is active. There is no per-form switch. A field counts as an email field when the Salesforce field it maps to has "email" in its API name (Email, SuppliedEmail, Personal_Email__c), and as a phone field when it has "phone" (Phone, MobilePhone). In the browser, a field whose label contains "Email" or "Phone" also counts.

Empty fields always pass. Whether a field is required is decided by the field's own Required Field setting, not by this plugin.

Phone minimum

The phone check requires 10 digits unless you set phoneMinDigits at the top level of the plugin record's Configuration Schema. Many international numbers are shorter than 10 digits, so lower it, or set it to 0 to turn the phone check off:

{
  "phoneMinDigits": 8
}

phoneMinDigits applies to both checks: the one in the browser, which shows its message when the visitor leaves the field and blocks the submit, and the one on the server.

Cross-Field Rules

Cross-field rules enforce relationships between form fields. Add them to the plugin record's Configuration Schema as a crossFieldRules array at the top level. Fields are named by Field ID:

{
  "crossFieldRules": [
    {
      "type": "match",
      "fields": ["email", "confirmEmail"],
      "message": "Email addresses must match"
    },
    {
      "type": "dateAfter",
      "fields": ["startDate", "endDate"],
      "message": "End date must be after start date"
    },
    {
      "type": "requiredIf",
      "field": "companyName",
      "condition": {
        "field": "accountType",
        "operator": "equals",
        "value": "Business"
      },
      "message": "Company name is required for business accounts"
    }
  ]
}

Rule Types

Type Description
match Every field listed in the fields array must have exactly the same value. Commonly used for email confirmation.
dateAfter The second field's date must be later than the first field's date. The rule is skipped while either field is empty or does not hold a date.
requiredIf The target field becomes required when the condition is true. Operators: equals (the default), notEquals, contains, isNotEmpty, and isEmpty. A rule with any other operator is skipped, with a warning in the browser console.

A rule that fails blocks the submit and shows its message. Cross-field rules and character limits run in the browser only. The server checks email, phone, and the regex rules below.

Character Limits

Configure character limits for text fields in a characterLimits object at the top level, keyed by Field ID:

{
  "characterLimits": {
    "description": {
      "max": 500,
      "warnAt": 400,
      "showCounter": true
    },
    "notes": 1000
  }
}

A plain number sets max. warnAt defaults to 80% of max, and showCounter defaults to true. The counter is the default color below warnAt, amber from warnAt, and red once the limit is reached. A value longer than max shows "Maximum 500 characters allowed (512 entered)." and blocks the submit.

Server-Side Validation

The Apex class implements IQuikFormsFieldValidator. QuikForms runs it on the server for each submitted field of every form, once the form's own checks (required fields and each field's Validation Regex) have passed. Fields hidden by a dependency rule and custom HTML fields are skipped. Its plugin record sets Validation Mode to Sync, so QuikForms calls its validateSync method during the submission.

A failed check refuses the submission, and its message comes back on that field in the same way as a failed Required Field or Validation Regex check. The server checks are:

  • Email: the value must be a well-formed address. Message: "Please enter a valid email address."
  • Phone: the value must contain at least phoneMinDigits digits (10 unless configured). Message: "Phone number must contain at least 10 digits."
  • Custom regex rules, below.

Custom Regex Rules

Add regex rules to the plugin record's Configuration Schema as a customRegexRules array at the top level:

{
  "customRegexRules": [
    {
      "field": "zipCode",
      "pattern": "\\d{5}(-\\d{4})?",
      "message": "Please enter a valid US zip code (e.g., 90210 or 90210-1234)"
    },
    {
      "field": "employeeId",
      "pattern": "EMP-\\d{6}",
      "message": "Employee ID must be in the format EMP-000000"
    },
    {
      "field": "Website",
      "pattern": "https?://[\\w.-]+\\.[a-zA-Z]{2,}(/.*)?",
      "message": "Please enter a valid URL starting with http:// or https://"
    }
  ]
}
  • field is a Field ID, or the API name of the Salesforce field that form fields map to. A rule without field applies to every filled field on every form, so always set it.
  • pattern must match the whole value, as if it began with ^ and ended with $. That differs from a field's Validation Regex setting, which passes when the pattern is found anywhere in the value. Patterns use Apex (Java) regex syntax, and every backslash is written twice inside JSON.
  • message is what the visitor sees. Without it, the visitor sees "Value does not match the required pattern."
  • A pattern that does not compile is skipped rather than blocking every submission.
  • The rules run on the server only, so the visitor sees the message after pressing Submit.
  • The shape {"serverValidation": {"regexRules": [ ... ]}} is also accepted.

A single field can carry its own rules: put a customRegexRules array in the field's Plugin Configuration (JSON), and it is used for that field instead of the plugin record's rules. The same box can hold {"fieldType": "email"} or {"fieldType": "phone"} to have the server treat a field as an email or phone field whatever it maps to. The box is on the field editor's Options & Structure tab.

Error Behavior: Fatal. Error Behavior decides what happens when the plugin's own code fails, not when a check fails. A failed check always refuses the submission, whatever this field says. With Fatal, an error thrown inside the validator also refuses the submission, and it is logged to QuikForms Error Manager as PLUGIN-6002 at Critical severity. Warn would let the submission through and log it at Medium severity; Skippable would let it through and log it at Low severity.

Lifecycle Hooks

Hook What It Does
onFormLoad Reads the configuration, adds the plugin's styles, and sets up the character counters
onFieldChange Clears the plugin's messages under the field that changed
onFieldBlur Shows the email typo suggestion, the phone digit message, and the character limit message for the field the visitor left
onValidate Checks email format, phone digits, and character limits on every filled field and runs the cross-field rules; any failure blocks the submit. The Apex validator then runs on the server

Submission Enrichment

Submission Enrichment Plugin

Apex Submission Handler

Submission Enrichment fills in fields before the record is created and acts after it is saved. It stamps the submission time, the visitor's browser user agent, their IP address, and the referring page, changes the case of text answers, and can create a follow-up Task related to the new record.

Download Plugin

Features

  • Metadata stamping. The plugin writes the submission time, the visitor's browser user agent, their IP address, and the HTTP referrer into fields you name.
  • Case transformations. Answers are converted to uppercase, lowercase, or title case before the record is created.
  • Follow-up Task creation. The plugin creates a Salesforce Task related to the new record, with a subject and due date you configure.
  • Runs before the record exists. Stamping and case changes happen in onBeforeSubmit, so the record is created with them in place.
  • Runs after the record is saved. Task creation happens in onAfterSubmit, when the new record's ID is known.

Submission handlers also run when a survey is submitted. There the record is the survey response (Survey__c), so the fields you name must exist on that object.

Configuration

Put these keys at the top level of the plugin record's Configuration Schema. Every key is optional.

Key Type Description
timestampField String The field to stamp with the submission time. Name it by its Salesforce API name so the plugin can match the value to the field's type: a Date/Time field gets the date and time, a Date field gets the date, and a text field gets ISO 8601 text in UTC, such as 2026-09-24T14:30:00Z
userAgentField String The field for the visitor's browser user agent string. On Experience Cloud forms it is filled only when the form's Log User Browser Info setting is on
ipAddressField String The field for the visitor's IP address
referrerField String The field for the HTTP referrer of the submission, which is normally the address of the form page itself
caseTransformFields Object Maps Field IDs to "uppercase", "lowercase", or "titlecase". Title case capitalizes the first letter of each word and lowercases the rest
createFollowUpTask Boolean Whether to create a follow-up Task after a successful submission
taskSubject String Subject of the follow-up Task. Default: "Follow up on form submission"
taskDaysUntilDue Integer Days from submission until the Task is due. Default: 3

How fields are named

  • The four stamping keys name a field on the form's object by its Salesforce API name. They can instead name a Field ID on the form, which writes to the field that form field maps to. On a multi-object form, only a Field ID is written.
  • The keys of caseTransformFields are always Field IDs, because they change an answer the visitor gave. The changed value goes to the field that form field maps to.

Experience Cloud forms

The IP address and referrer are filled on Experience Cloud forms as on Salesforce Sites forms. The user agent is filled on an Experience Cloud form only when the form's Log User Browser Info setting is on. The timestamp, case transformations, and the follow-up Task work on both.

Full Configuration Example

{
  "timestampField": "Submitted_At__c",
  "userAgentField": "Browser_Info__c",
  "ipAddressField": "Submitter_IP__c",
  "referrerField": "Referrer_URL__c",
  "caseTransformFields": {
    "firstName": "titlecase",
    "lastName": "titlecase",
    "company": "titlecase",
    "state": "uppercase"
  },
  "createFollowUpTask": true,
  "taskSubject": "Review new form submission",
  "taskDaysUntilDue": 3
}

Metadata Stamping Only

{
  "timestampField": "Submitted_At__c",
  "userAgentField": "Browser_Info__c",
  "ipAddressField": "Submitter_IP__c",
  "referrerField": "Referrer_URL__c"
}

Case Transforms Only

{
  "caseTransformFields": {
    "firstName": "titlecase",
    "lastName": "titlecase",
    "email": "lowercase",
    "state": "uppercase"
  }
}

Task Creation Only

{
  "createFollowUpTask": true,
  "taskSubject": "Follow up on lead submission",
  "taskDaysUntilDue": 5
}

Where the Task is related

The Task is related to the new record through its Related To field (WhatId). A Task cannot be related to a Lead or a Contact that way, so on a Lead or Contact form no Task is created and the submission still succeeds. On a multi-object form the Task is related to the parent record.

Error Behavior: Warn. If the plugin's code throws an error, the submission still goes ahead and the error is logged to QuikForms Error Manager as PLUGIN-6002 at Medium severity. A field name that does not exist on the object is not a plugin error: QuikForms skips that one value, creates the record, and logs APEX-2004 at Low severity. The default Exception Log Level (Error) records neither. To see them, raise Exception Log Level in the QuikForms Setup Assistant under Settings → Data & Logging: Warning records Medium entries, and Debug also records Low ones.

Lifecycle Hooks

Hook What It Does
onBeforeSubmit Stamps the metadata fields and applies the case transformations before the record is created
onAfterSubmit Creates the follow-up Task related to the new record, with the configured subject and due date

External Verification

External Verification Plugin

JS Apex Hooks Callout

External Verification checks a field's value against an external service, such as an email, phone, or domain verification API, when the visitor leaves the field. The browser asks the plugin's Apex class to make the call, so no credential is ever in the browser, and shows the result in a note under the field. It never blocks a submission.

Download Plugin

How It Works

External Verification follows a three-step flow:

  1. JavaScript trigger (onFieldBlur). When a configured field loses focus and has a value, the browser code waits for the field's debounceMs, if set, then calls QuikFormsPlugins.callout() with the value and the field's verification type.
  2. Apex callout (executeCallout). The Apex class sends the value to the configured endpoint or Named Credential and returns the service's JSON response to the browser.
  3. Result display. The note under the field reads "Verifying..." while it waits, then shows a check mark or a warning sign with a message (see API Contract).

The field's value is sent to the service you configure, outside Salesforce. Choose a service your data policies allow.

Configuration

Put these keys at the top level of the plugin record's Configuration Schema:

Key Type Required Description
endpoint String Yes* Base URL of the verification API. Required unless namedCredential is set. Add the host in Setup → Remote Site Settings, or Salesforce refuses the callout.
namedCredential String No API name of a Salesforce Named Credential to call instead of endpoint, with or without a callout: prefix. Takes precedence over endpoint.
verificationTypes Object No Maps each verification type to a path added to the endpoint, so each type can have its own endpoint.
timeoutMs Integer No HTTP timeout in milliseconds. Used only when the record's Timeout (ms) field is blank; the record ships with it set to 5000, which wins.
fields Array Yes The fields to verify. Each entry takes the keys below.

Field entries

Key Description
fieldId The Field ID to verify. fieldName is accepted as another name for it.
objectField Instead of fieldId: verify any form field that maps to this Salesforce field API name, such as Email.
verificationType Sent to the service as type, and used to look up the path in verificationTypes. Default: "default".
path A path for this field alone. Overrides the verificationTypes lookup.
debounceMs Milliseconds to wait after the visitor leaves the field before calling. Leaving the field again within that time replaces the pending call, so the service is called once.

The plugin record ships with Supports Callout selected. QuikForms refuses the callout if it is cleared.

Full Configuration Example

{
  "endpoint": "https://verify.example.com/v1",
  "verificationTypes": {
    "email": "/verify/email",
    "phone": "/verify/phone",
    "domain": "/verify/domain"
  },
  "fields": [
    { "fieldId": "email", "verificationType": "email", "debounceMs": 500 },
    { "objectField": "Phone", "verificationType": "phone" },
    { "fieldId": "website", "verificationType": "domain", "debounceMs": 500 }
  ]
}

Using Named Credentials

For production, use a Named Credential instead of a raw endpoint URL. Salesforce then stores the service's authentication and adds it to each call, and no Remote Site Setting is needed.

{
  "namedCredential": "VerifyServiceCredential",
  "fields": [
    { "fieldId": "email", "verificationType": "email", "path": "/verify/email" }
  ]
}
Named Credentials and public forms: a public form runs as the site's guest user, which cannot use an External Credential by default. Give the guest user access to the External Credential's principal through a permission set assigned to that user. Without it the callout fails on the public site while working when you preview the form as yourself.

API Contract

Request (sent by Apex)

The Apex class sends a POST to the endpoint plus the path, with Content-Type: application/json and this body:

{
  "value": "user@example.com",
  "type": "email"
}

Response (expected from the service)

The service returns a JSON object. The plugin reads three possible success flags and a message, and ignores anything else:

{
  "verified": true,
  "message": "Email address is valid and deliverable"
}
Response Note under the field
verified, valid, or success is true Check mark with the response's message, or "Verified"
Any other 2xx response Warning sign with the response's message, or "Could not verify"
The call fails: a status outside 2xx, a timeout, or a network error Warning sign with the failure message, such as "Verification service returned status 503"
Error Behavior: Skippable. Verification is advice for the visitor and never blocks a submission. A failed call shows its message in the note under the field. Only an error that escapes the plugin's Apex is written to QuikForms Error Manager, as PLUGIN-6002 at High severity.

Lifecycle Hooks

Hook What It Does
onFormLoad Reads the configuration and adds the plugin's styles
onFieldBlur For a configured field with a value, waits out debounceMs, shows "Verifying...", makes the callout, and shows the result. A newer result for the same field replaces an older one

The plugin has no onValidate hook, so verification results are not part of the submission and do not affect it.

Progress Indicator

Progress Indicator Plugin

JS Hooks

Progress Indicator shows how many of the form's required fields the visitor has filled in, as a progress bar, a row of dots, or a count. It can stay pinned while the visitor scrolls, and can pulse once when the last required field is filled. On a multi-page form it follows the visitor from page to page.

Download Plugin

Features

  • Three display modes. Choose a progress bar, a row of dots, or a count to match your form's design.
  • Required field tracking. The indicator counts the visible required fields. Optional fields and custom HTML are left out, and a checkbox counts as filled when it is checked.
  • Dependency awareness. Fields hidden by a dependency rule are left out of the count, and the count updates as rules show or hide fields.
  • Multi-page aware. On a multi-page form it counts the required fields on the current page and updates on every page change. Set scope to "form" to count the whole form instead.
  • Animated transitions. The bar slides to its new width and filled dots grow slightly.
  • Optional sticky positioning. The indicator can stay at the top of the visible area while the visitor scrolls.
  • Completion pulse. Optionally, the indicator pulses once when the count first reaches 100%.

Display Modes

The label line reads "3 of 8 fields complete". When the count covers only the current page of a multi-page form, it reads "3 of 5 fields on this page complete". showLabel turns the label line on or off.

Bar Mode

The label sits above a horizontal bar that fills left to right. The bar has no percentage text; 3 of 8 fills it to 38%.

  3 of 8 fields complete
  [===========-------------------]

Dots Mode

The label is followed by one dot per required field. A filled dot is a completed field, and hovering over a dot shows that field's label.

  3 of 8 fields complete
  (*) (*) (*) ( ) ( ) ( ) ( ) ( )

Fraction Mode

The count appears as larger text, with the completed number in the form's highlight color and no graphic. It repeats the label's wording, so set showLabel to false with this mode:

  3 of 8 fields complete

Configuration

Replace the contents of the plugin record's Configuration Schema with a JSON object of the options you want, at the top level. An option you leave out takes its default.

Option Type Default Description
mode string "bar" Display mode: "bar", "dots", or "fraction"
sticky boolean false Keep the indicator at the top of the visible area while the visitor scrolls
showLabel boolean true Show the label line (e.g., "3 of 8 fields complete")
celebrateOnComplete boolean false Pulse the indicator once when all required fields are filled
position string "top" Where the indicator goes in the form's plugin area: "top" (first) or "bottom" (last)
scope string "page" Multi-page forms only: "page" counts the required fields on the current page, "form" counts every page at once. A single-page form is the same either way

Configuration Example

{
  "mode": "bar",
  "sticky": true,
  "showLabel": true,
  "celebrateOnComplete": true,
  "position": "top",
  "scope": "page"
}

CSS Variables

The Progress Indicator takes its colors and corner radius from the form's theme, through these QuikForms CSS variables:

Variable Used for
--qf-highlight-color The bar's fill, the dots' outline and fill, and the completed number in fraction mode
--qf-input-bg The bar's track, empty dots, and the background when sticky is on
--qf-text-color The label and the fraction text
--qf-radius-sm The bar's rounded corners

Lifecycle Hooks

Hook What It Does
onFormLoad Reads the configuration, adds the indicator to the form's plugin area, and calculates the starting count, which includes fields that start with a value
onFieldChange Recalculates the count from the value the form just reported and updates the display
onFieldBlur Recalculates, picking up any field a dependency rule has just shown or hidden
onPageChange Recalculates for the page that just became active on a multi-page form
Multi-page forms: the indicator counts the required fields on the page the visitor is looking at and updates on every page change, so the progress reflects what they can see. The review page has no fields of its own, so the whole-form count is shown there. Set scope to "form" to show progress across every page all the time.

Writing Plugins for Both Renderers

An Experience Cloud form renders each field inside its own shadow root, which document.getElementById and document.querySelector cannot see into. The browser code of the plugins above reaches its elements through methods on QuikFormsPlugins that both renderers provide, or, for Range Slider, through the container QuikForms passes it, which is why the plugins work on Experience Cloud and Salesforce Sites alike. Build your own plugins the same way.

Element API

Method Returns
getFieldElement(fieldId) The field's own input, select, or textarea, or null
getFieldContainer(fieldId) An element where the plugin may add its own UI for that field, such as a message or a badge. Look up the plugin's elements inside it, not in document
getFormArea() An element for UI that belongs to the whole form, such as a progress bar. On Experience Cloud it sits at the top of the form
addStyles(id, css) Nothing. Adds the plugin's CSS once per id, wherever the plugin's elements are rendered. On Experience Cloud this reaches inside each field's shadow root, which a <style> added to document.head cannot
QuikFormsPlugins.register('myPlugin', {
  hooks: {
    onFormLoad: function (ctx) {
      QuikFormsPlugins.addStyles('my-plugin-styles', '.my-plugin-note { color: #b45309; }');
    },
    onFieldBlur: function (ctx) {
      var container = QuikFormsPlugins.getFieldContainer(ctx.fieldId);
      if (!container) return null;
      var note = container.querySelector('.my-plugin-note');
      if (!note) {
        note = document.createElement('div');
        note.className = 'my-plugin-note';
        container.appendChild(note);
      }
      note.textContent = ctx.value ? '' : 'Please add a value';
      return null;
    }
  }
});

Where a hook gives you a value (ctx.value, ctx.fieldValues), use it rather than reading the control. On Experience Cloud the value the form reports through the hook is the one it submits.

Custom Field Types

A plugin registers field types under fieldTypes. The name you register is what the field editor's Plugin Field Type stores, and a QuikForms Custom Field Type metadata record (Plugin, Field Type Name, Display Label, Category, Default Configuration) lists it in that picker. Range Slider's zip includes one to copy.

QuikFormsPlugins.register('starRating', {
  fieldTypes: {
    starRating: {
      // Returns the field's HTML as a string
      render: function (field, ctx) {
        return '<div class="stars"><input type="hidden" class="stars-value" value="">...</div>';
      },
      // Runs once, after the HTML is in place
      initialize: function (fieldId, container) { /* attach listeners */ },
      // The container is the LAST argument on both renderers
      getValue: function (fieldId, container) {
        return container.querySelector('.stars-value').value;
      },
      setValue: function (fieldId, value, container) {
        container.querySelector('.stars-value').value = value;
      },
      validate: function (fieldId, field, container) {
        return { isValid: true, errors: [] };
      }
    }
  }
});
  • render(field, ctx) returns an HTML string. field includes id, label, required, and pluginConfiguration, the field's Plugin Configuration (JSON) exactly as stored. ctx includes locale and placeholder.
  • initialize(fieldId, container) runs once, after the markup is in the page.
  • getValue, setValue, and validate receive the field's container as their last argument on both renderers. Find your elements with container.querySelector, never through document. validate returns { isValid, errors }, where errors is a list of messages.
  • Report changes. Fire a bubbling input or change event from your control when its value changes. Experience Cloud reads getValue on those events; Salesforce Sites reads it at submit.
  • The HTML is not sanitized: it is code you installed, not visitor or form content.
  • A field whose plugin is not active renders as its own field type.

The complete JavaScript API, including every hook, is in the Field Type Interface and Available Hooks references.