Forms
Create forms with conditional sections, collect answers and send email notifications in WordPress.
Build forms with the WordPress block editor. Save answers in WordPress, send email notifications and connect add-ons.
Create a form
Go to XPAC → Forms → All Forms → Add New Form. Give the form a title.
Add your fields. Select each field to set its label, name and rules in the block sidebar. Give each field a unique name.
Add a Submit block. In the form settings sidebar, set your success message, email notifications and any other options you need.
Publish the form. Edit a page, add the Form block and select your form. Publish or update the page too.
Open the page while signed out and send a test entry. Check it under XPAC → Forms → Submissions. If you added email notifications, check your inbox and the Deliveries tab.
The form itself must be published to appear for visitors. An editor's preview can show an unpublished form.
Add fields
Select a field to edit its Label, Name, Description, Placeholder and Required setting. A placeholder is an example, so keep a clear label too.
The Name connects a field to saved answers, email variables and add-ons. If you rename it, update anything that uses it.
| Field | Use |
|---|---|
| Text | One line, with optional length limits or a custom pattern. |
| Textarea | A longer answer. |
| An email address; optionally allow only one submission per address. | |
| Phone | A phone number, checked for the visitor's country and saved as +CC digits; fixed national formats too. See phone numbers. |
| URL | A web address. |
| Number | A number, with optional minimum and maximum values. |
| Date / Time | A date or time, with optional limits. |
| Dropdown / Radio Buttons | One choice. Set a label and saved value for each option. |
| Checkboxes | Several choices. Required means at least one must be checked. |
| Consent | An agreement checkbox, with an optional link. |
| Confirmation | Repeat another field's answer to check it matches. |
| File Upload | Files, with type, size and count limits. |
| Hidden | A preset value the visitor does not see. |
| Dynamic Select | Choices from WordPress terms, posts, users or a REST endpoint. |
Use Submit to send the form and Reset to clear the current answers. Groups, Columns, Headings and Paragraphs help organise the layout.
Fields are checked when the form is sent. Under Validation, you can also check
while visitors type or leave a field. For custom Text patterns, add a Pattern
message that explains the expected format, such as AB-1234.
For File Upload, leaving allowed types empty uses WordPress's allowed types. Your host's upload limit still applies. See uploads and privacy before collecting private files.
For Dynamic Select, choose a source and how to display the choices. A REST source needs a non-empty JSON list and the property names to use as labels and values. A server on another domain must allow browser requests through CORS.
Visitors can change hidden values and remote choices in their browser. Do not use those values alone to decide permissions or prices.
Phone numbers
A new Phone field uses Smart international. Visitors pick their country
from a list beside the field, and the number is formatted as they type. A number
typed or pasted with + and a country code picks the country by itself.
When the form is sent, the number is checked for that country. A number that
cannot be dialled there shows "Invalid phone number".
Select the field to set two options below Mask:
- Default country: the country the list starts on. Leave it on Detect from the visitor to choose one for each visitor.
- Allowed countries: accept numbers from these countries only. A number from any other country shows "Phone numbers from this country are not accepted". Leave it empty to accept every country. Guernsey, Jersey and the Isle of Man share +44 with the United Kingdom, but their numbers count as their own countries, so allow them separately if you accept them.
The number is shown beside the country's code, so it leaves out the leading
0 (or other trunk prefix) dialled only inside the country: a visitor in
Armenia who types 077 123456 sees 77 123456 next to +374. While the field is
empty it shows a number from the selected country as an example, such as
77 123456, unless you set your own Placeholder. Typing stops at the
longest number that country has.
Without a default country, the list starts on the country of the visitor's time zone, worked out in the browser with no location lookup: a visitor in Yerevan starts on Armenia whatever language their browser is in. If that country is not allowed, it tries the country of the browser language, then your site's country, taken from Settings → General → Site Language (English (UK) gives the United Kingdom), then the first allowed country. If none of these fits, the list shows Select a country and the visitor types the number with its country code.
Every answer is saved the same way: +, the country code and the digits, with no
spaces. 07400 123456 typed for the United Kingdom is saved as +447400123456.
Submissions, email notifications, CSV exports and add-ons all get that number.
Phone fields already on your forms keep their format until you change it:
- Any text (not checked) accepts whatever is typed and saves it as typed.
- United States (US) masks the number as
(555) 010-1234while visitors type and saves it that way.
To switch a field, select it, choose Smart international under Mask and update the form. Answers saved earlier stay as they were.
User Profiles fields that use a Phone field support
Smart international too: the profile form shows the country list, and the profile
keeps the + number.
Good to know:
- In a CSV export, a number that starts with
+gets a'in front, so spreadsheet apps read it as text and not as a formula. Excel, Numbers and Google Sheets do not show the'. - Map phone answers to a text or phone field in a connected service. A number
field there, such as an Airtable number column with Send numbers as numbers
on, keeps the digits and drops the
+. - Visitors who browse with JavaScript off type into a plain box. A number with
+and a country code is always read correctly. A number without one is read in the field's default country, then your site's country. On a multilingual site (WPML, Polylang), the site's country can follow the language the form was sent in, so set a default country on the field if your visitors type local numbers.
Conditional logic
Conditional logic is built into Forms. A Conditional Logic block controls a group of fields, and an Execute if section on a delivery action — a notification, redirect, message, or a connected add-on's action — gates that one action. Both use the same rule builder: choose whether All or Any rules must match, then choose a source field, operator, and comparison value.
Conditional logic used to ship as the separate XPAC: Conditional Logic plugin. While that plugin is still active it keeps doing the work, and Forms shows a notice on the Plugins screen. Deactivate and delete it when convenient: saved forms keep their conditions.
Conditional form sections
Insert a Conditional Logic block inside a form and place the fields or content it should control inside the block.
Choose Show, Hide, or Disable, then choose All or Any.
Test both matching and non-matching paths, including leaving optional sources empty.
| Appearance | When the rule matches | When it does not match |
|---|---|---|
| Show | Section is visible and enabled. | Section is hidden and its controls are disabled. |
| Hide | Section is hidden and its controls are disabled. | Section is visible and enabled. |
| Disable | Section remains visible but its controls are disabled. | Section remains visible and enabled. |
Disabled or hidden controls are removed from validation and from the accepted submission. Their answers do not reach entries, notifications, or integrations. Nested conditional sections are resolved repeatedly to a stable result rather than being limited to a fixed nesting depth.
Show sections take no space until their rule is evaluated, avoiding a flash of protected content. Hide and Disable sections are visible from the start, since a visitor whose rule does not match should see them.
A condition is not an authorization boundary
Rule names, operators, and comparison values are serialized into published form markup so the browser can react immediately. A visitor can inspect them. Never put a password, private token, permission decision, or other secret into a condition. Enforce authorization in server-side application code.
Password and confirmation controls are not offered as condition sources. A legacy or externally written condition that names one is treated as unresolvable in both the browser and server; the credential is never compared with a value stored in form content or settings.
Comparisons
| Operator | Behaviour |
|---|---|
| is / is not | Case-insensitive scalar equality; choice groups match selected values. |
| greater / greater or equal | Orders two numbers, two ISO dates (YYYY-MM-DD), two times (HH:MM[:SS]) or two date-times; any other pair, an unanswered field included, does not match. |
| lower / lower or equal | Orders two numbers, two ISO dates (YYYY-MM-DD), two times (HH:MM[:SS]) or two date-times; any other pair, an unanswered field included, does not match. |
| contains | Substring match for text; for a checklist, whether that option is ticked, however many boxes are ticked. |
| starts with / ends with | Case-insensitive text prefix or suffix. |
For numbers, use numeric fields and values. For checkboxes, radio groups, and selects, choose one of the field's actual option values. An optional field that is present in the form but unanswered remains a resolvable empty answer on the server, so browser and server use the same rule inputs.
Gate an action
Notification, redirect, and message items, and the actions contributed by each connected add-on, gain an Execute if section. An action with no condition follows its normal enabled status. A valid condition must match before that one action runs; sibling actions and entry storage are independent.
Stale and malformed rules
The editor shows a warning when a saved dependency points to a renamed or deleted field, or to a provider block whose plugin is inactive. Repair the rule before publishing.
At runtime:
- an unresolvable rule is non-matching;
- an All group fails when any dependency is unresolvable;
- an Any group may still match another resolvable dependency;
- an action condition with malformed rows or an invalid relation returns disabled;
- malformed JSON or shape on one conditional block hides and disables that block without preventing other form sections from initializing.
For a Show section, non-matching means hidden. For Hide or Disable, non-matching leaves the section usable. This preserves the configured appearance semantics while preventing a broken rule from unexpectedly executing a gated action.
When a visible field is removed by an outer condition, its name becomes unavailable to every later nested pass and action check. Conditional logic and Multi-Step Form also keep independent disable ownership, so making one condition visible cannot re-enable a field that a skipped step still owns.
The browser applies the same rule: a field inside a hidden section or a skipped step is unresolvable there too, and a rule on a field whose controls load later, such as a REST Dynamic Select, is bound once those controls appear.
Provider values
Some add-ons expose a provider value rather than a normal form field. For example, User Targeting can expose the resolved visitor country. Those values appear in the same rule builder when their provider is active and publishes both browser and server data. If the provider disappears, the dependency becomes unresolvable and the editor warns.
Developer reference
The block stores appearance, relation, and dependencies:
{
"appearance": "show",
"relation": "and",
"dependencies": [
{
"name": "plan",
"operator": "=",
"value": "business"
}
]
}An action stores the same condition object without appearance:
{
"condition": {
"relation": "and",
"dependencies": [
{ "name": "plan", "operator": "=", "value": "business" }
]
}
}The server re-evaluates wrapper visibility during
xpac_forms_initialize_submission and gates actions through
xpac_forms_action_item_status. xpac_forms_submission_invisible_blocks takes a
third argument, $hidden, naming the field names the previous pass found hidden,
which a listener must treat as unanswerable. Nothing is applied until two passes
agree, since nested conditions can take more than one pass to settle.
The front end exposes form.xpacForm.condition.check() for add-ons that use the
same runtime comparison rules, and form.xpacForm.condition.initializeField(),
which returns a function that re-evaluates the rule. See the
Forms hooks reference for the full hook list.
Form settings
Open the form settings sidebar to change these options.
Availability and spam
Under Submissions, you can set an opening and closing date, limit the number of submissions, or accept only signed-in visitors. These options are off by default. Schedules use the site's timezone; spam and deleted entries do not count toward capacity.
Under Anti-spam protection, turn on Honeypot to catch bots with an invisible decoy field. For more checks, add Akismet, Cloudflare Turnstile or Google reCAPTCHA.
The site-wide attempt limit is in XPAC → Forms → Settings → Spam and abuse. It defaults to 20 attempts per visitor, per form, in five minutes.
Messages and redirects
Use Messages to set the success message and show it above or below the fields. Site-wide defaults are under XPAC → Forms → Settings → Messages. Leave a message blank to use the default wording.
Use Redirect to send visitors to another page after success. A Redirect URL on a Submit block takes priority. Without a redirect, the visitor stays on the page and sees the success message.
Conditional logic lets you choose messages and redirects based on the visitor's answers.
Email notifications
Under Email Notifications, add a notification and set To, From, subject and message. You can also set Reply-To, Cc, Bcc, and plain-text or HTML format. Check the prefilled addresses before publishing.
Use Variables to insert an answer by field name or a built-in value:
| Variable | Value |
|---|---|
{{ADMIN_EMAIL}} | Site administrator's email. |
{{NOREPLY_EMAIL}} | A no-reply address on your domain. |
{{SITE_TITLE}} | Site title. |
{{REFERRER}} | Page where the form was filled in. |
{{ALL_FIELDS}} | All saved answers with their labels. |
Including a File Upload field in the message, directly or through
{{ALL_FIELDS}}, attaches its files. Confirmation and Password values are never
saved or emailed.
Email is sent in the background after the answer is saved. Delivered means WordPress handed off the email; check the recipient's inbox to confirm it arrived.
After sending
The Form builder, the block that holds the whole form, sets what visitors see once the form is sent and they stay on the page. In the form editor, select it, for example in List View. In its Settings tab, After sending has two choices:
| Choice | What visitors see |
|---|---|
| Reset the form | The answers clear and the success message shows. A form with steps goes back to its first step. The default. |
| Show the message only | The success message, which stays on screen. Everything else in the form hides, including fields, steps and buttons. |
With Show the message only, a form with no success message to show resets instead. Reloading the page shows the form again.
Manage forms and answers
Go to XPAC → Forms:
| Tab | Use |
|---|---|
| Overview | Submission totals, recent entries and delivery status. |
| All Forms | Create, edit, preview, duplicate, publish or delete forms. |
| Submissions | Read answers, add stars and notes, mark spam or export CSV. |
| Deliveries | Check email and add-on jobs, read errors and retry failures. |
| Settings | Set retention, spam limits, default messages and appearance. |
In Submissions, choose a form and filter by status, date or field values. CSV export uses your current filters and includes fields hidden from the table. Notes are excluded from CSV and print output.
Permanently deleting a form also deletes its submissions. Trashing a form keeps them. A restored form returns as a draft; publish it again to show it to visitors.
A form User Profiles uses for sign-in shows its kind, such as Registration, after its #id.
Retry a delivery
Open Deliveries, read the error and fix the cause. Use Retry now, or Retry all failed for the current form and provider filters. Dismiss clears an outstanding failure without sending it again.
Retry data expires after 30 days. If a provider timed out, check whether it already received the entry before retrying to avoid duplicates.
If deliveries stay pending, check Tools → Scheduled Actions and make sure WordPress cron is running. A delayed delivery does not remove the saved submission.
Access
Administrators can manage submissions and site-wide settings. Other roles need
xpac_manage_form_submissions to open Overview, Submissions and Deliveries. Form
editing follows WordPress post permissions.
Connect add-ons
Most account credentials go under XPAC → Integrations. Set the action and field mapping in the form's sidebar, then send a test entry and check Deliveries and the destination.
- Form behaviour: Multi-Step Form and Auto Submit. Conditional logic is built into Forms.
- WordPress: Submission To Post and User Profiles.
- Payments and data: Stripe, Airtable, Webhooks and Zapier.
- Email services: Mailchimp, Brevo, Campaign Monitor, GetResponse, Klaviyo, SendGrid, TouchBasePro and Zoho.
Each add-on's guide covers its setup. Use a test destination for actions that send messages, create records or take payments.
Style forms
The form editor's Styles sidebar controls labels, descriptions, inputs and validation messages. These styles apply to all forms on the site. Submit and Reset buttons have their own block styles.
To style one form only, select its Form builder, for example in List View, and open the Styles tab. The Fields panel sets the fields' text, background, border and focus border colours, placeholder, label and description colours, border width, radius and label size. The colours, border width and radius show the site's value until you change them, and anything you leave alone keeps the theme's look. The panel's menu resets one setting or all of them. The editor shows the same look visitors get.
To use your own CSS, turn off XPAC → Forms → Settings → Appearance → Use the
built-in form styling. Theme authors can also set values in theme.json; see the
styling reference.
Uploads and privacy
Under Settings → Submissions, set Keep submissions for to choose a retention
period. The default, 0, keeps them until deleted. Expired entries move to Trash
before permanent deletion, or are deleted immediately if WordPress trash is disabled.
Delete uploaded files with the submission is on by default. Files used by other submissions or as featured images are kept.
Uploads are public to anyone with their URL by default. Turn on Keep uploaded files private for new uploads; existing files are not moved. Private download links require permission to manage submissions.
Apache can use the protection file Forms creates. On nginx, ask your host to block direct access to the private folder:
location ~ /wp-content/uploads/xpac-forms-private/ {
deny all;
return 404;
}Test that a file's direct URL is blocked while signed out and its Forms download link works for an authorised user.
Forms does not use WordPress's personal-data export or erase tools. Delete entries in Submissions, and handle copies in email, backups and connected services separately. Removing the plugin does not delete its data.
Import and export
In All Forms, choose Export as JSON from a form's menu or select several forms for bulk export. On the other site, choose Import.
Imports create new forms. They include fields, settings, notifications and add-on configuration, but no submissions. The limit is 100 forms and 5 MB per file.
Exports can contain secrets saved on the form, such as webhook headers. Keep the file private and check recipients, redirects and add-on actions after importing.
Troubleshooting
| Problem | Check |
|---|---|
| Visitors cannot see the form | Publish the form itself. Check its schedule, capacity and sign-in limit. |
| A file is rejected | Check allowed types, field limits and your host's upload limit. |
| Dynamic choices do not load | Check the REST URL, JSON properties, permissions and CORS. |
| Email does not arrive | Check Deliveries, the site's mail setup and the recipient's spam folder. |
| An add-on fails | Read the error in Deliveries, check its credentials and mapping, then retry. |
| Forms shows a database update notice | Ask your host to check that WordPress can create and update the Forms tables. |
Developer reference
See the JavaScript API, Forms hooks, Form block and field block reference.