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:
- 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). - Deploy every folder in the zip, together, in one deployment. With the Salesforce CLI, copy the folders into the
force-app/main/defaultdirectory of a Salesforce DX project and runsf 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. - Deploying Apex to a production org runs Apex tests, so every plugin that has Apex includes its test class. Deploy it with the class.
- 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
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 PluginFeatures
- 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-4567is stored as5551234567. 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.
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:
- A data attribute on the field's input
- The plugin record (recommended)
- 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
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 PluginFeatures
- 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:
- Open the field in the field editor, from QuikField Manager or from the field's edit button in the form builder.
- 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.
- 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.
- 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
}
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
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 PluginFeatures
- 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
phoneMinDigitsdigits (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://"
}
]
}
fieldis a Field ID, or the API name of the Salesforce field that form fields map to. A rule withoutfieldapplies to every filled field on every form, so always set it.patternmust 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.messageis 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.
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
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 PluginFeatures
- 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
caseTransformFieldsare 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.
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
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 PluginHow It Works
External Verification follows a three-step flow:
- 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 callsQuikFormsPlugins.callout()with the value and the field's verification type. - 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.
- 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" }
]
}
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" |
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
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 PluginFeatures
- 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
scopeto"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 |
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.fieldincludesid,label,required, andpluginConfiguration, the field's Plugin Configuration (JSON) exactly as stored.ctxincludeslocaleandplaceholder.initialize(fieldId, container)runs once, after the markup is in the page.getValue,setValue, andvalidatereceive the field's container as their last argument on both renderers. Find your elements withcontainer.querySelector, never throughdocument.validatereturns{ isValid, errors }, whereerrorsis a list of messages.- Report changes. Fire a bubbling
inputorchangeevent from your control when its value changes. Experience Cloud readsgetValueon 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.