Skip to content
Ultivo Toolkit 1.0 is here! Get 10% off your first year with LAUNCH10 until September 30. See pricing
Documentation menu

Forms

Front-end forms run on the same field engine as everything else. You build the fields directly in the form, submissions are stored as entries, and you read them back with the regular API (ultivo_field(), ultivo_get_field()).

Building a form#

Ultivo Toolkit → Forms → Add New Form. Give it a title: the slug becomes the shortcode ID.

The Fields tab is a three-column builder: a field palette on the left, a live canvas in the middle, and a settings panel on the right. Drag a field from the palette onto the canvas, or click it to append. The canvas is rendered by the same code that renders the front end, so what you see is what a visitor gets.

Selecting a field opens its settings in four tabs:

  • General: label, field name, type, description, required.
  • Validation: minimum and maximum length, a regular expression pattern with your own error message.
  • Presentation: width, "start on a new row", hide label, hide the required asterisk, CSS class and id.
  • Logic: show this field only when another field has a particular value.

Field types#

The palette is deliberately limited to what a visitor can fill in:

  • Basic: Text, Text Area, Email, Phone, Number, URL, File Upload, Hidden
  • Choice: Select, Radio, Checkbox, True/False, Button Group
  • Date & time: Date Picker, Time Picker, Date Time Picker
  • Layout: HTML, Repeater

Developer-facing types (relational, media, flexible content, group, clone, password, color) don't exist in forms.

Two fields in a form cannot share a name: they would write to the same key on the entry. The builder marks both in red, on the canvas and in the settings panel, and tells you why. Saving still works.

A Hidden field can carry context instead of a fixed value. Use one of these tokens as its default value: {post_id}, {post_title}, {user_id}, {referrer}, or {url:parameter} for a query-string value. Tokens are only ever resolved in the default, never in something a visitor submitted.

Columns#

Give a field a width and it shares a row with its neighbours. Tick Start on a new row to force a line break before it.

File uploads#

Uploaded files do not go into the media library. They are stored in a randomly named folder under uploads/ultivo-forms/, protected against direct access, and downloadable only through the admin by someone who can edit that entry. Executable extensions are always refused, whatever you allow in the field settings.

One thing to watch: with Save Entries off there is nothing left pointing at the file, so uploads are deleted right after the notification is sent. The editor warns you when a form combines the two.

Settings#

The Settings tab holds the form-level options: submit button label, success message, Save Entries (keep submissions), and Require Login (only logged-in visitors can submit).

Submitting without a reload#

By default a submission reloads the page: WordPress processes it, redirects, and the confirmation appears where the form was. That redirect is deliberate: pressing F5 afterwards cannot send the submission twice.

For a form in a popup or modal that is exactly wrong. The reload closes the popup, and the confirmation renders inside the thing that just disappeared, so the visitor sees nothing and has no idea whether it worked.

Tick AJAX Submit under Settings (Submit without reloading the page) and the submission is sent in the background instead. Then choose what happens next:

  • Replace the form with the success message: the form disappears and the confirmation takes its place. What you want in a popup, and nobody can submit twice.
  • Keep the form, clear it and show the message above it: for a form someone fills in several times in a row.

Validation errors appear the same way: in place, with everything the visitor typed still there.

It is off by default, so existing forms keep behaving exactly as they do now. Uploads and all three spam providers work unchanged. Without JavaScript (or if the server sends something unexpected) the form falls back to a normal submit, so a visitor never ends up with a form that silently does nothing.

Front-end styling is here too. Tick Disable default styling to style the form entirely yourself; conditional logic keeps working, because the rules that hide fields are printed inline rather than coming from the stylesheet. Two things can be kept: the date picker's calendar, and the column layout. Without the latter, a field's width is written as data-width="50" instead of an inline style, so your own CSS can pick it up without fighting a specificity battle.

Notifications#

The Notifications tab sends an email for every submission. Placeholders for the body: {all_fields}, {field:field_name}, {form_title}, {site_title}, {admin_email}, {entry_id}. An empty body defaults to all fields.

Spam protection#

Pick one provider (Google reCAPTCHA v2, Google reCAPTCHA v3 or Cloudflare Turnstile) under Forms → Integrations, and it applies to the whole site. Each form has a Spam Protection checkbox under Settings, on by default, so a form can opt out. See Integrations.

Every form also carries a honeypot field that needs no configuration at all.

Showing a form#

[ultivo_form id="contact"]
<?php ultivo_form( 'contact' ); ?>          // prints directly in a template
$html = ultivo_form( 'contact', false );    // or returns the markup as a string

Forms require the forms suite feature to be enabled.

Under the hood, each form registers its fields as a hidden field group (group_form_{slug}), so the rest of the engine (storage, ultivo_field(), repeaters) works unchanged. Those groups never appear in the field groups UI.

Reading entries#

Read entries with the same functions used for any other field. Pass the entry ID as the second argument, and the field name is the one you gave the field in the form builder:

$entries = get_posts( [ 'post_type' => 'ultivo-form-entry', 'posts_per_page' => -1 ] );
foreach ( $entries as $entry ) {
    echo esc_html( ultivo_field( 'email', $entry->ID ) );
}