Freeform Freeform for Craft

Templating

Form object

The Form object contains its metadata and field objects. You can either render the form using the pre-selected formatting template by calling form.render() or achieve a more fine-grained control over it by iterating over its rows and fields and using form.renderTag and form.renderClosingTag methods.

TIP

Freeform will automatically insert javascript in the footer of the page for features such as Spam Protection, Submit disable on click, and other special fieldtypes. If you prefer to have this load inside the <form></form> tags, you can adjust the Freeform Javascript Insertion Location setting.

Properties

name

Outputs the name of the form.

handle

Outputs the handle of the form.

id

Outputs the unique ID of the form.

description

Outputs the description of the form.

submissionTitleFormat

Outputs the submission title format used when creating new submissions based on this form.

returnUrl

Outputs the return URL of the form.

successMessage

Outputs the custom success message configured in the Validation tab for your form inside the form builder.

errorMessage

Outputs the custom error message configured in the Validation tab for your form inside the form builder.

hasErrors

A bool variable, which will be true if there are any errors in any of the fields or the form. It might be true even if form.errors list is empty, (e.g. if one or more fields have an error). This can be used in conjunction with the Errors object for displaying special and general errors.

pages

Returns a list of Page objects each containing its label and index.

currentPage

Returns the current Page object containing its label and index.

duplicate Renamed and Revised in 5.0+

A bool variable, which will be true if the form has the Duplicate Check setting enabled (duplicate submission check) and the user has already submitted the form.

Template Overrides New in 5.0+

The form and each field have the ability to control attributes, values and more at the template level. Each one of them is entirely optional. There are several namespaces:

  • attributes - whatever you specify here will be set as an attribute on the form.
  • buttons - controls the output of the Submit button(s).
  • fields - controls the output of all Fields.
  • captchas - add attributes to the main Captcha wrapper automatically inserted by Freeform when using reCAPTCHA or hCaptcha.

TIP

Please see the Template Overrides documentation for detailed instructions.

User Guide:

Need to pass a custom property to a formatting template, e.g. toggle display of the form title, etc?

Usage in Templates

Simple Render

A basic implementation might look like this:

{% set form = freeform.form("myFormHandle") %}

{{ form.render() }}
1
2
3

Override Classes & Values

Once Template Overrides have been implemented, your code might look something like this:

{% set form = freeform.form("myFormHandle") %}

{{ form.render({
    attributes: {
        novalidate: true,
        class: "my-form-class",
    },
    buttons: {
        attributes: {
            submit: { class: "form-field-button blue" },
            back: { class: "form-field-button gray" },
        },
    },
    fields: {
        "@global": {
            attributes: {
                input: {
                    class: "form-field-input",
                },
                label: {
                    class: "form-field-label",
                },
            },
        },
        ":required": {
            attributes: {
                label: { "+class": "form-field-required" },
            },
        },
        ":errors": {
            attributes: {
                input: { "+class": "form-field-is-invalid" },
            },
        },
        "@dropdown": {
            attributes: {
                container: {
                    "data-select-container": true,
                },
                input: {
                    "+class": "form-field-select fullwidth",
                },
            },
        },
        "@checkboxes, @radios" : {
            attributes: {
                input: {
                    "+class": "form-field-options",
                },
            },
        },
        "myFieldHandle": {
            value: entry.id,
        },
    },
}) }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

Manually Iterate

Manually iterate through form fields inside your regular template:

{# Set the form #}
{% set form = freeform.form("myFormHandle") %}

{# Render the opening form tag #}
{{ form.renderTag({
    attributes: {
        row: { class: "freeform-row" },
        success: { class: "freeform-form-success" },
        errors: { class: "freeform-form-errors" },
    },
    buttons: {
        attributes: {
            container: { class: "freeform-button-container" },
            column: { class: "freeform-button-column" },
            buttonWrapper: { class: "freeform-button-wrapper" },
            submit: { class: "freeform-button-submit" },
            back: { class: "freeform-button-back" },
            save: { class: "freeform-button-save" },
        },
    },
    fields: {
        "@global": {
            attributes: {
                container: { class: "freeform-column" },
                input: {
                    novalidate: true,
                    class: "freeform-input",
                },
                label: { class: "freeform-label" },
                instructions: { class: "freeform-instructions" },
                error: { class: "freeform-errors" },
            },
        },
        ":required": {
            attributes: {
                label: { "+class": "freeform-required" },
            },
        },
        ":errors": {
            attributes: {
                input: { "+class": "is-invalid has-validation" },
            },
        },
        "@group": {
            attributes: {
                label: { "+class": "group-label" },
            },
        },
        "@signature": {
            attributes: {
                input: { "-class": "freeform-input" },
            },
        }
    },
}) }}

{# Success and error message handling for non-AJAX forms #}
{% if not form.settings.ajax %}
    {% if form.submittedSuccessfully %}
        <div{{ form.attributes.success }}>
            <p>{{ form.settings.successMessage | t('freeform') }}</p>
        </div>
    {% endif %}
    {% if form.hasErrors %}
        <div{{ form.attributes.errors }}>
            <p>{{ form.settings.errorMessage | t('freeform') }}</p>

            {% if form.errors|length %}
                <ul>
                    {% for error in form.errors %}
                        <li>{{ error }}</li>
                    {% endfor %}
                </ul>
            {% endif %}
        </div>
    {% endif %}
{% endif %}

{# Render page tabs if multi-page #}
{% if form.pages|length > 1 %}
    <ul class="freeform-pages">
        {% for page in form.pages %}
            <li {% if form.currentPage.index == page.index %}class="active"{% endif %}>
                {% if form.currentPage.index == page.index %}
                    <b>{{ page.label }}</b>
                {% else %}
                    {{ page.label }}
                {% endif %}
            </li>
        {% endfor %}
    </ul>
{% endif %}

{# Build form rows and fields #}
{% macro renderRows(form, rows) %}
    {% for row in rows %}
        {% set width = (12 / (row|length)) %}
        <div{{ form.attributes.row }}>
            {% for field in row %}
                {% do field.setParameters({
                    attributes: {
                        container: { class: [
                            "freeform-column-" ~ width,
                            "freeform-fieldtype-" ~ field.type,
                        ]},
                    },
                }) %}
                {% if field.type == "group" %}
                    <div class="freeform-group">
                        <label{{ field.attributes.label }}>
                            {{ field.label }}
                        </label>
                        <div>
                            {{ _self.renderRows(form, field.layout) }}
                        </div>
                    </div>
                {% else %}
                    {{ field.render }}
                {% endif %}
            {% endfor %}
        </div>
    {% endfor %}
{% endmacro %}

{# Display form field rows and columns #}
{{ _self.renderRows(form, form.rows) }}

{# Render the closing form tag #}
{{ form.renderClosingTag }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

Success Flash

When not using AJAX, the session success flash message (displays only once) when the form is successfully submitted:



 
 
 



{% set form = freeform.form("myForm") %}

{% if form.submittedSuccessfully %}
    <div>You've successfully submitted this form!</div>
{% endif %}

{{ form.render }}
1
2
3
4
5
6
7

Error when Duplicate

Display a message when the submission is a duplicate:



 
 
 
 




{% set form = freeform.form("myForm") %}

{% if form.duplicate %}
    <div class="alert alert-warning duplicate">
        You've already submitted this form!
    </div>
{% else %}
    {{ form.render }}
{% endif %}
1
2
3
4
5
6
7
8
9