Vora Forms

Vora Forms documentation

A drag-and-drop form builder that stays out of the way — fields, conditional logic, multi-step forms, entries, notifications and spam protection.

Version1.1.0WordPress6.0+PHP7.4+LicenceGPLv2 or later
Get Vora Forms

Install

  1. 1Upload the vora-forms folder to /wp-content/plugins/, or install the zip from Plugins → Add New → Upload Plugin.
  2. 2Activate the plugin.
  3. 3Go to Vora Forms → Add New and build your first form.
  4. 4Copy the shortcode and paste it into any page or post.

The five screens

All under one top-level Vora Forms menu.

All Forms

Every form you have built, with its shortcode ready to copy.

Add New

The drag-and-drop builder.

Entries

Stored submissions, with search, filtering and CSV export. Suspected spam is filed here rather than deleted.

Leads

What was forwarded to a mailing list or CRM, and what failed.

Settings

Site-wide defaults: storage and retention, spam defaults for new forms, sender identity and email styling, lead-destination credentials, the accent colour, and the advanced options.

Building a form

Drop fields onto the canvas and drag them into the order you want. Each field can be full, half or third width, so two or three sit side by side on a row — a first and last name pair, or a city, county and postcode line.

A field exists when its driver does

The builder offers a field type exactly when the server has a driver able to validate and store it. Nothing can be placed on a form that the server would then reject.

The 14 field types

Texttext

A single line.

Emailemail

Validated as an address on the server, not only in the browser.

Paragraphtextarea

Multi-line free text.

Numbernumber

Numeric input.

Phonetel

Telephone number.

Websiteurl

A URL.

Dropdownselect

One choice from a list.

Multiple choiceradio

One choice, shown as radio buttons.

Checkboxescheckbox

Any number of choices, including none.

Datedate

A date picker.

Hiddenhidden

Carries a fixed value through with the submission — a campaign name or a source, typically.

Headingheading

A section title. Presentational: it collects nothing.

HTMLhtml

Arbitrary markup between fields, for an explanation or a consent notice.

Page breakpage-break

Splits the form into steps. See Multi-step forms below.

Conditional logic

Show or hide a field based on an earlier answer — reveal “Which product?” only after someone picks Support, or ask for a company name only from business enquiries. Because the form is rendered in the browser, the field appears and disappears as the visitor types, with no page reload.

Multi-step forms

Add a Page break field wherever you want the form to split. Everything before it becomes step one, everything after it step two, and so on. Progress can be shown as a bar or as numbered steps.

Steps are worth reaching for when a form is long enough that seeing all of it at once would put someone off — the count of fields on screen is what people judge, not the total.

Placing a form

Two ways, and they render the same form:

Shortcode[vforms_form id="12"]

Copy it from All Forms and paste it into any page, post or widget area. The ID is the form's post ID.

Block

A Vora Forms block in the editor, for anyone who would rather pick the form from a list than paste a shortcode.

The old tag still works[lite_form]

Answered for pages written before the rename, so nothing that already exists stops rendering. New work should use [vforms_form].

Entries

Storing submissionsstore_entries · default on

Every accepted submission is saved and appears under Entries, searchable and filterable, and exportable to CSV.

Retentionentry_retention · default 0

Days to keep entries; 0 keeps them forever. Set a real number if the form collects anything you would rather not hold indefinitely — a retention period is the simplest data-protection control there is.

Turning storage off

The form still works and notifications still send; nothing is kept on the site. Do that only when you are certain the notification email is reaching someone, because there is then no second copy.

Notifications

Two kinds, set per form: an admin notification to you, and an autoresponder to the person who submitted. Both support merge tags in the subject, the body and the recipient list.

From name and addressfrom_name · from_email

Who notifications come from. Delivery goes through wp_mail(), so an SMTP plugin handles the actual sending.

Email accentemail_accent · default #0f766e

Separate from the form's accent on purpose: an email is read on a white card in an inbox with none of the page around it, so what works on the site does not automatically work here. Header, row dividers and link colours are derived from this one value, and every shade is a solid hex rather than rgba() so Outlook renders it like everyone else.

Footer noteemail_footer_note · default “Sent from {site_name} ({site_url})”

Yours to write, and it supports merge tags. Clear it and the footer row disappears entirely rather than leaving an empty strip.

These tags are available in every template:

  • {form_title}
  • {form_id}
  • {entry_id}
  • {site_name}
  • {site_url}
  • {admin_email}
  • {user_ip}
  • {date}

Field values are available as tags too, so an autoresponder can open with the name the visitor just gave you.

Lead destinations

A submission can be forwarded to a mailing list or CRM as well as stored: Mailchimp, HubSpot, Brevo, ActiveCampaign, MailerLite, Kit, or any endpoint of your own through the webhook destination. The Leads screen shows what was forwarded and what failed.

Why the keys are site-level, not per-form

A form’s schema is duplicated, exported, and editable by anyone who can edit forms. An API key stored there would travel with all three. Credentials live in Settings instead, and each key is read by exactly one driver.

Spam protection

Five layers, and the ones in Settings are the defaults applied to newly created forms — an existing form keeps what it was saved with.

Honeypothoneypot · default on

A field a person never sees and a bot fills in. Submissions caught by it are dropped without a trace, and the visitor is shown the normal success message — so a bot learns nothing about why it failed.

Timing checktime_trap · default 3 seconds

A submission that arrives faster than a human could have typed it is treated as spam. Raise it for long forms, lower it for a single-field signup.

Rate limitingrate_limit · default 10

Submissions allowed per visitor before further attempts are refused.

Captchacaptcha · default none

Per form, either a math question or distorted image characters. The challenge is fetched by the browser after the form loads rather than printed into the page, so a page cache cannot freeze it and hand every visitor the same question. The answer stays on the server, and each challenge is good for exactly one attempt — right or wrong — so a solved one cannot be replayed.

Akismetakismet · default off

Uses your existing Akismet installation when one is present.

Suspected spam is filed, not deleted

Every spam filter has false positives, and a real enquiry that silently vanishes is worse than one sitting in a folder. The honeypot is the deliberate exception: those submissions are dropped without a trace.

Appearance

One accent colouraccent_color · default #0f766e

Themes the whole form. Hover, focus-ring and tint shades are derived from it, and the label colour on buttons and checkboxes flips between white and dark automatically — whichever gives more contrast against the accent you picked. There is no combination of settings that produces unreadable text.

Why teal-700 and not teal-600

The lighter shade cannot reach 4.5:1 against white or against dark text, so it has no accessible label colour at all. The default is the darker one for that reason.

Front-end stylesheetload_frontend_css · default on

Turn it off if you would rather style the form entirely from your theme.

Advanced

Trust proxy headerstrust_proxy_headers · default off

Turn this on when the site is behind Cloudflare or another proxy and every visitor appears to share one IP. Leave it off otherwise: on a site that is not behind a proxy, those headers can be forged by anyone, and believing them lets a bot walk straight past the rate limiter.

Caching

The submit endpoint does not depend on a nonce, so a form served from a cached page still submits correctly.

Extending it

Field types come from a small registry, so an add-on can supply its own:

add_action( 'vforms_register_fields', function () {
    require_once __DIR__ . '/class-field-rating.php';
    Vora_Forms_Field_Registry::register( 'rating', 'My_Field_Rating' );
} );

And there is an action after each accepted submission:

add_action( 'vforms_submission', function ( $entry_id, $form_id, $fields ) {
    // Push to a CRM, and so on.
}, 10, 3 );

A field appears in the builder exactly when a driver exists for it, so registering one is all that is needed — there is no second list to keep in step.

Troubleshooting

Does the form work with caching plugins?+

Yes. The submit endpoint does not depend on a nonce, so a form served from a cached page still submits correctly. The captcha is fetched after load for the same reason.

Does it work without JavaScript?+

No. The form is rendered by a small React bundle, which is what makes conditional logic and multi-step navigation work without a page reload. Visitors with JavaScript disabled see a short notice instead.

Can I use my own email provider?+

Yes. Vora Forms sends through wp_mail(), so any SMTP or API mailer plugin you have installed handles delivery and logging with no extra configuration.

My site is behind Cloudflare and every visitor has the same IP+

Turn on Settings → Advanced → Trust proxy headers. It is off by default because on a site that is not behind a proxy those headers can be forged by anyone, which would let a bot walk straight past the rate limiter.

The image captcha option is missing+

It is hidden entirely on servers without the GD extension, rather than offered and silently downgraded. Use the math question, which is the better choice anyway — a screen reader can read it aloud, and an image captcha cannot be made accessible.

A real enquiry was marked as spam+

It is in Entries, filed rather than deleted — every spam filter has false positives, and an enquiry that silently vanishes is worse than one sitting in a folder. Honeypot catches are the single exception: those are dropped without a trace.