freeform.form function

The freeform.form template function returns a Form object containing its metadata and fields objects. From there you can either render the form using the pre-selected formatting template by calling form.render() or take control over it by iterating over its fields and using form.renderTag and form.renderClosingTag methods.

Watch the Rendering a Freeform Form Manually Course tutorial on CraftQuest!

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.

Form

Parameters

The freeform.form template function is always constructed the same way. The first assumed parameter should contain the form ID or handle, and the second parameter (optional) should contain an object of overrides (typically used for applying a class globally to specific types of inputs, etc).

So following this format: {{ craft.freeform.form("FORMHANDLE", {OVERRIDES}) }}, your code might look something like this:

{{ craft.freeform.form("composerForm", {
	labelClass: "form-label",
	inputClass: "form-control",
	instructionsBelowField: true,
	overrideValues: {
		hiddenFieldHandle: entry.id,
	}
}).render() }}
1
2
3
4
5
6
7
8
  • First parameter: formID or formHandle #
  • Second parameter (optional) is an object of the following overriding options:
    • inputClass #
      • Overrides the class name of all input elements.
    • submitClass #
      • Overrides the class name of all submit elements.
    • rowClass #
      • Overrides the class name of all row <div> elements.
    • columnClass #
      • Overrides the class name of all field column <div> elements.
    • labelClass #
      • Overrides the class name of all <label> elements.
    • errorClass #
      • Overrides the class name of all error <ul> elements.
    • instructionsClass #
      • Overrides the class name of all instruction <div> elements.
    • instructionsBelowField #
      • A boolean value, if set to true - will render field instructions below the <input> element.
    • class #
      • Overrides the <form> class name.
    • id #
      • Overrides the <form> ID attribute.
    • returnUrl #
      • Overrides the return URL for the form.
    • method #
      • Overrides the <form> method attribute. POST by default.
    • name #
      • Overrides the <form> name attribute. POST by default.
    • action #
      • Overrides the <form> action attribute.
    • status 2.5.0+ #
      • Overrides the default status of the generated submission.
      • Can be either an ID or handle of an existing Status record.
    • overrideValues #
      • Allows you to override the value inside Text fields, or pre-select a default option for multi-option field types (specify option values in this case). E.g.:
        • hiddenFieldHandle: entry.id - pull in an entry ID from a Craft Entry.
        • stateSelect: "AZ" - pre-select Arizona state in a State select field.
        • availability: ["tue", "thu"] - pre-check Tuesday and Thursday checkbox options in a checkbox group field type.
        • firstName: currentUser.name - pull in the currently logged in user's name into the Name field.
      • Specify the field handle as key, and provide the custom value override as its value.
      • If a Field uses an overrideValue attribute, it will take precedence over the value specified in this attribute.
    • formAttributes #
      • An object of attributes which will be added to the form.
      • Ex: formAttributes: { "novalidate": true, "data-form-id": "test" }
    • inputAttributes #
      • An object of attributes which will be added to all input fields.
      • Ex: inputAttributes: { "readonly": true, "data-field-id": "test" }
    • useRequiredAttribute: true #
      • Adds required attribute to input fields that have been set to be required in Composer.
    • fieldIdPrefix: 'myform-' 2.2.0+ #
      • Adds a prefix value on field outputs. Helpful if you have more than 1 form on the same template and are sharing fields.
    • dynamicNotification: { recipients: ["admin@example.com", "support@example.com"], template: "test.html" } #
      • Allows using a dynamic template level notification for a more fine-grained control.
      • Hard code values or pass a value from another element such as an Entry.
      • For Database entry based templates, specify the handle for template.
      • For Twig file based templates, specify the full file name including .html for template.
      • NOTE: this feature uses Session data. It will likely not work properly if the page is cached with something like Varnish, etc.

TIP

If displaying the exact same form more than once in a single template, some of the <form> tag attributes set on one form may carry over to other ones. To work around this, you can unset the attribute on the other forms (unless they have their own attributes set). For example, if one form has class: 'something', it may end up applying to other instances of the form, but you can add class: null to those others to work around it.

Usage in Templates

Render the form using its formatting template:

{{ craft.freeform.form("composerForm").render() }}
1

Render the form using its formatting template, but overriding some classes:

{{ craft.freeform.form("composerForm", {
	labelClass: "form-label",
	inputClass: "form-control",
	instructionsBelowField: true,
	submitClass: "btn btn-success",
	overrideValues: {
		hiddenFieldHandle: entry.id,
	}
}).render() }}
1
2
3
4
5
6
7
8
9

Get the form object and manually iterate through fields:

{% set form = craft.freeform.form("composerForm", {
	id: "myform",
	class: "form-class",
	rowClass: "sample-row-class",
	submitClass: "button",
}) %}

{{ form.renderTag }}

{% if form.hasErrors %}
	<div class="freeform-form-has-errors">
		{{ "Error! Please review the form and try submitting again."|t('freeform') }}

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

{% for row in form %}
	<div class="{{ form.customAttributes.rowClass }}">
		{% for field in row %}
			{% set columnClass = "sample-column " ~ form.customAttributes.columnClass %}
			{% if field.type == "submit" %}
				{% set columnClass = columnClass ~ " submit-column" %}
			{% endif %}

			<div class="{{ columnClass }}"{{ field.rulesHtmlData }}>
				{{ field.render({
					class: field.type != "submit" ? "freeform-input" : "",
					labelClass: "sample-label" ~ (field.required ? " required" : ""),
					errorClass: "sample-errors",
					instructionsClass: "sample-instructions",
				}) }}
			</div>
		{% endfor %}
	</div>
{% endfor %}


{{ 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

Form formatting can also be extremely manual, if that is something you prefer. Here's an example of different levels of manual you can use:

{% set form = craft.freeform.form("composerForm") %}

{{ form.renderTag({returnUrl: "contact/success"}) }}

	{% if form.hasErrors %}
		<div class="freeform-form-has-errors">
			{{ "There was an error submitting this form"|t }}

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

	{% set firstName = form.get("firstName") %}
	{% set company = form.get("company") %}
	{% set lastName = form.get("lastName") %}
	{% set recipients = form.get("recipients") %}

	<label>{{ firstName.label }}</label>
	<input name="{{ firstName.handle }}" value="{{ firstName.value }}" />
	{{ firstName.renderErrors() }}

	<label>{{ lastName.label }}</label>
	<input name="{{ lastName.handle }}" value="{{ lastName.value }}" />
	{{ lastName.renderErrors() }}

	{{ company.renderLabel() }}
	{{ company.renderInput() }}
	{{ company.renderErrors() }}

	<label>Email Address</label>
	<input name="email" />
	{{ form.get("email").renderErrors() }}

	<label>Phone</label>
	<input name="phone" />
	{% if form.get("phone").hasErrors %}
		This field is required!
	{% endif %}

	<label>Recipient</label>
	<select name="{{ recipients.handle }}" type="dynamic_recipients">
	{% for recipients in recipients.options %}
		<option value="{{ loop.index0 }}">{{ recipients.label }}</option>
	{% endfor %}
	</select>

	<button type="submit">Submit</button>

{{ 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
Last Updated: 4/26/2019, 11:54:00 PM