Template Overrides

Replace Elzo Forms front-end templates from a theme and adjust template loading with filters.

Free & PRO · Developer Last reviewed

Elzo Forms renders its front-end markup from template files, and a theme can replace any of them by placing a file with the same name in an elzo-forms folder. Use a template override when a hook or CSS cannot produce the markup you need.

Before you begin

  • Put overrides in a child theme, or in a theme you control. Overrides placed in a parent theme you did not write are lost when that theme updates.
  • Do not edit files inside the Elzo Forms plugin folder. Plugin updates replace them.

How overrides are located

When Elzo Forms loads a front-end template, it looks for the file in this order and uses the first one it finds:

  1. wp-content/themes/your-child-theme/elzo-forms/TEMPLATE
  2. wp-content/themes/your-parent-theme/elzo-forms/TEMPLATE, checked only when a child theme is active
  3. the plugin’s own templates/TEMPLATE

Because the plugin copy is the final fallback, you only need to copy the templates you actually intend to change.

Override a template

  1. Create an elzo-forms folder in your theme.
  2. Copy the template you want to change out of the plugin’s templates folder, keeping its filename and any subfolder.
  3. Edit your copy.
  4. Load a page containing a form and confirm your version is being used.

For example, to change the markup around a single field:

wp-content/plugins/elzo-forms/templates/field.php
  →  wp-content/themes/your-child-theme/elzo-forms/field.php

To change how one field type renders, keep the field-types subfolder:

wp-content/themes/your-child-theme/elzo-forms/field-types/field-select.php

Templates you can override

Template Renders
form.php The form wrapper, the <form> element, hidden inputs, and the steps container.
step.php One step, including its fields.
field.php The wrapper around a single field: label, required marker, and helper text.
field-types/field-text.php The Text field control, for every Text variant.
field-types/field-textarea.php The Textarea field control.
field-types/field-select.php The Select field control.
field-types/field-checkbox.php The Checkbox field control.
field-types/field-radio.php The Radio field control.
field-types/field-range.php The Range field control.
field-types/field-file.php The File Upload field control.
field-types/field-hidden.php The Hidden field control.
field-types/field-content.php The Content field.
field-types/field-button.php The Button field.

A field type template is chosen by the base type, so an Email or Date field — a Text variant — renders through field-text.php, which receives the variant as $field_subtype. These templates are the same in Elzo Forms Free and Elzo Forms PRO.

Templates that cannot be overridden

Admin templates are always loaded from the plugin, so nothing in the form builder can be replaced this way. The submission notification template, templates/email/admin-email.php, is also loaded directly rather than through the template loader; customize submission emails with the email filters instead. See Email Customization Hooks.

Variables available in a template

Each template receives its data as ordinary local variables. The docblock at the top of every plugin template lists what that template gets, which is the reliable place to check after an update.

form.php receives $form_id, $form_post, $form_object, $form_settings, $texts_settings, $style_settings, $steps, $steps_total, $form_wrapper_styles, $form_attr_id, $form_attr_class, $form_data_attrs_string, $form_instance_suffix, and $atts.

step.php receives $step, $step_index, $steps_total, $form_id, $form_settings, $texts_settings, and $form_instance_suffix.

field.php and the field type templates receive the field’s prepared data, including $field, $field_data, $field_type, $form_id, $step_index, $id, $label, $required, $under_label, $under_field, $shows_label_wrapper, $shows_under_field, $form_settings, $texts_settings, and $form_instance_suffix.

Repeated forms and $form_instance_suffix

The same form can be rendered more than once on a page. $form_instance_suffix is empty for the first copy and holds -2, -3 and so on for later copies. Elzo Forms appends it to every ID it generates — the form, each field and wrapper, the nonce input — so the IDs stay unique and labels point at the input of their own copy. The $id a field template receives already includes it.

An override that builds IDs of its own should append the suffix too, and an override of form.php or step.php has to pass it on to the next template, as the plugin templates do.

Keep the parts the form depends on

Templates produce markup that the plugin’s own JavaScript and submission handler rely on. When you edit a copy, keep the following intact:

  • the nonce input and the hidden action, elzo_form_id, elzo_form_time, and elzo_form_settings inputs in form.php;
  • the form’s id and class attributes, and the data attributes produced by $form_data_attrs_string;
  • the field wrapper attributes returned by $field->get_wrapper_attributes( $form_instance_suffix ), which carry the state used by conditional logic;
  • each input’s name attribute;
  • $form_instance_suffix, passed from form.php to step.php and on to each field.

Warning: removing the nonce input, the hidden inputs, or an input’s name attribute causes submissions to be rejected or saved without that field’s value.

Also escape output in your copy exactly as the original does. Templates render visitor-supplied values, and dropping esc_html() or esc_attr() introduces a cross-site scripting vulnerability.

Filters

Five filters and actions adjust how templates are located and loaded. They apply to front-end templates only; admin templates bypass all of them.

elzo_forms/templates/theme_dir

Changes the folder name searched inside the theme. The default is elzo-forms.

add_filter( 'elzo_forms/templates/theme_dir', function () {
    return 'my-theme-forms';
} );

Register it anywhere that runs before a form is rendered, including a theme’s functions.php.

Most sites do not need this filter. Use the default elzo-forms folder unless the name conflicts with something already in your theme.

elzo_forms/templates/locate_template

Overrides the resolved template path. Receives the theme template path found so far (an empty string when none was found), the template name, the template path, and the template arguments. Return an absolute path to a readable file, or an empty string to fall through to the plugin template.

add_filter( 'elzo_forms/templates/locate_template', function ( $template, $template_name, $template_path, $args ) {
    if ( 'field.php' === $template_name && ! empty( $args['field_type'] ) && 'file' === $args['field_type'] ) {
        $custom = plugin_dir_path( __FILE__ ) . 'templates/field-upload.php';

        if ( file_exists( $custom ) ) {
            return $custom;
        }
    }

    return $template;
}, 10, 4 );

elzo_forms/templates/template_args

Filters the variables passed into a template before they are extracted. Receives the arguments array, the template name, and the resolved template path. Return the array.

add_filter( 'elzo_forms/templates/template_args', function ( $args, $template_name, $template ) {
    if ( 'field.php' === $template_name ) {
        $args['under_field'] = '';
    }

    return $args;
}, 10, 3 );

elzo_forms/templates/before_template and elzo_forms/templates/after_template

Actions that run immediately before and after a template is included. Both receive the template name, the template path, the resolved file path, and the arguments. Use them to output markup around a template without copying it.

add_action( 'elzo_forms/templates/before_template', function ( $template_name, $template_path, $template, $args ) {
    if ( 'form.php' === $template_name ) {
        echo '<div class="my-form-frame">';
    }
}, 10, 4 );

add_action( 'elzo_forms/templates/after_template', function ( $template_name, $template_path, $template, $args ) {
    if ( 'form.php' === $template_name ) {
        echo '</div>';
    }
}, 10, 4 );

Maintaining an override

An override pins your copy of the markup at the version you copied it from. When Elzo Forms updates a template, your copy keeps rendering the old markup, and a change the plugin relies on may be missing from it.

Each plugin template carries a @version in its docblock. After a plugin update, compare that version with the one in your copy and merge any changes.

Version 1.1.0 changed six templates: form.php, step.php, field.php, field-types/field-checkbox.php, field-types/field-radio.php and field-types/field-file.php. Copies made from 1.0.0 keep working, but miss the following:

  • form.php, step.php and field.php pass $form_instance_suffix along. Without it, a second copy of the same form on a page repeats the IDs of the first.
  • field.php renders the label of a Checkbox or Radio field as an element with the ID {$id}-label instead of a <label>, and field-checkbox.php and field-radio.php mark their option lists as a group labelled by it, so screen readers announce the question with the options.
  • field-file.php gives the remove button an accessible name and exposes the upload progress bar to screen readers.

Because of this maintenance cost, prefer the smallest change that works: CSS first, then the template filters above or a field type filter, and a full template copy last.

Next steps