Nette Documentation Preview


Nette Forms greatly facilitates creating and processing web forms. What it can really do?

  • validate sent data both client-side (JavaScript) and server-side
  • provide a high level of security
  • multiple render modes
  • translations, i18n

Nette Framework puts a great effort to be safe and since forms are the most common user input, Nette forms are as good as impenetrable. All is maintained dynamically and transparently, nothing has to be set manually. Well-known vulnerabilities such as Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF) are filtered, as well as special control characters. All inputs are checked for UTF-8 validity. Every multiple-choice, select box and similar are checked for forged values upon validating. Sounds good? Let's try it out.

With Nette Forms, you can diminish routine tasks like double validation on both server and client-side. You can also avoid common mistakes and security issues.

First Form


composer require nette/forms

Let's create a simple registration form for our app. Forms are added into presenters using component factories:

use Nette\Application\UI;

class HomepagePresenter extends UI\Presenter
	// ...

	protected function createComponentRegistrationForm(): UI\Form
		$form = new UI\Form;
		$form->addText('name', 'Name:');
		$form->addPassword('password', 'Password:');
		$form->addSubmit('login', 'Sign up');
		$form->onSuccess[] = [$this, 'registrationFormSucceeded'];
		return $form;

	// called after form is successfully submitted
	public function registrationFormSucceeded(UI\Form $form, \stdClass $values): void
		// ...
		$this->flashMessage('You have successfully signed up.');

render in template is done using control tag:

{control registrationForm}

and the result should look like this:

We have created a simple form with name and password form fields, which will call registrationFormSucceeded() after being submitted and successfully validated. The form itself is passed as the first parameter. The submitted values are passed as the second parameter contained in a ArrayHash object. If you prefer a simple array, you can typehint the second parameter array $values. You can also use $values = $form->getValues() to retrieve the submitted values.

In the context of presenter lifecycle, the form is processed on the same level as the processing of signals (handle* methods), that is right after the action* method and before the render* method.

The rendered form follows basic web accessibility guidelines. All labels are generated as <label> elements and are associated with their inputs, clicking on the label moves the cursor on the input.

Data stored in $values do not contain values of form buttons, so they're ready for more operations (such as inserting into the database). It is noteworthy that whitespace at both the left and right sides of the input is removed. Just try it out: put your name into the form field, add few spaces and submit it: the name will be trimmed.

Although we mentioned validation, our form has none. Let's fix it. To require a user's name, call setRequired() method on the form item. You can pass an error message as an optional argument and it will be displayed if the user does not fill his name in:

$form->addText('name', 'Name:')
	->setRequired('Please fill your name.');

Try submitting a form without the name – the message is displayed unless you meet the validation rules. The form is validated on both the client and server side.

If you don't use nette/sandbox, you need to link netteForms.js in order to enable JavaScript validation. You can find the file in src/assets folder.

<script src="/path/to/netteForms.js"></script>

Nette Framework adds required class to all mandatory elements. Adding the following style will turn the label of name input to red.

.required label { color: maroon }

Even though setting classes might be handy, we need solid data, don't we? We will add yet another validation rule, this time with addRule() method. The first argument sets what should the value look like, the second one is the error message again, shown if the validation is not passed. Preset validation rules will do this time, but you will learn how to create your own in no time.

The form will get another optional input age with the condition, that it has to be a number (Form::INTEGER) and in certain boundaries (Form::RANGE). This time we will utilize a third argument of addRule(), the range itself:

$form->addText('age', 'Age:')
	->addRule(Form::INTEGER, 'Your age must be an integer.')
	->addRule(Form::RANGE, 'You must be older 18 years and be under 120.', [18, 120]);

Obviously room for a small refactoring is available. In the error message and in the third parameter, the numbers are listed in duplicate, which is not ideal. If we were creating a multilingual form and the message containing numbers would have to be translated into multiple languages, it would make it more difficult to change values. For this reason, substitute characters can be used in this format:

	->addRule(Form::RANGE, 'You must be older %d years and be under %d.', [18, 120]);

We can set any HTML attributes to form fields. We can set the age input as numeric and add placeholder:

$form->addText('age', 'Age:')
	->setHtmlAttribute('placeholder', 'Please, fill in your age');

In the most advance browsers, namely Google Chrome, Safari and Opera, tiny arrows are rendered next to the input. Safari for iPhone shows an optimized keyboard with numbers.

Let's return to the password field, make it required, and verify the minimum password length (Form::MIN_LENGTH), again using the substitute characters in the message:

$form->addPassword('password', 'Password:')
	->setRequired('Pick a password')
	->addRule(Form::MIN_LENGTH, 'Your password has to be at least %d long', 3);

We will add one more input, passwordVerify, where the user will be prompted to enter his password once more, to check for the typo. Using validation rules, we will check if both fields contain the same value (Form::EQUAL). Notice the dynamic third argument, which is, in fact, the password control itself:

$form->addPassword('passwordVerify', 'Password again:')
	->setRequired('Fill your password again to check for typo')
	->addRule(Form::EQUAL, 'Password mismatch', $form['password']);

If the form would not be used for registration, but rather for editing records, it would be handy to set default values.

That's a complete, fully working registration form with both client-side and server-side validation. Automatically treats magic quotes, checks for invalid UTF-8 strings, etc. Everything is ready and without the slightest effort on our side – Nette has taken care of it.

Examples are available to download. Try adding some more form fields. Many inspiring forms are also in /examples/Forms of the distribution package.

Form Fields

Besides wide range of built-in form fields you can add custom fields to the form as follows:

$form = new Form;
$form['date'] = new DateInput('Date:');

Use the extension method to add a new method to object form:

Nette\Forms\Container::extensionMethod('addZip', function (Form $form, string $name, string $label = null) {
	return $form->addText($name, $label)
		->addRule($form::PATTERN, 'At least 5 numbers', '[0-9]{5}');

$form = new Form;
$form->addZip('zip', 'ZIP code:');

Fields are removed using unset:


Default Values

There are two ways to set default values. Method setDefaults() on a form or a container:

$form->addText('name', 'Name:');
$form->addInteger('age', 'Age:');

	'name' => 'John',
	'age' => '33'

or method setDefaultValue() on a single input:

$form->addEmail('email', 'Email:')

Default values of select-boxes and radio lists are set with the key from passed array of values:

$form->addSelect('country', 'Country', [
	'cz' => 'Czech republic',
	'sk' => 'Slovakia',
$form['country']->setDefaultValue('sk'); // country defaults to Slovakia

For CheckBox:

$form->addCheckbox('agree', 'Agree with conditions')

Another useful option is the „emptyValue“. If value of the form field after form submit is same as its „emptyValue“ the field behaves as not filled.

$form->addText('phone', 'Phone:')

Disabling Inputs

In order to disable an input, you can call $control->setDisabled().

$form->addEmail('email', 'E-mail:')->setDisabled();

The input cannot be written to and its value won't be returned by getValues().

If we need a read-only input (the input will be rendered as disabled but its value will be shown), we disable the input first and set the value afterwards. The reason is that setDisabled() method resets the value of the input.

$form->addText('readonly', 'Readonly:')->setDisabled()->setValue('readonly value');

If you only need to remove input's value but not disabling it, you can use setOmitted(). This option is useful for omitting antispam inputs for example.

$form->addText('antispam', 'Antispam:')->setOmitted();

Low-level Forms

To add an item to the form, you don't have to call $form->addXyz(). Form items can be introduced exclusively in templates instead. This is useful if you, for example, need to generate dynamic items:

{foreach $items as $item}
	<p><input type=checkbox name="sel[]" value={$item->id}> {$item->name}</p>

After submission, you can retrieve the values:

$values = $form->getHttpData($form::DATA_TEXT, 'sel[]');
$values = $form->getHttpData($form::DATA_TEXT | $form::DATA_KEYS, 'sel[]');

In the first parameter, you specify element type (DATA_FILE for type=file, DATA_LINE for one-line inputs like text, password or email and DATA_TEXT for the rest). The second parameter matches HTML attribute name. If you need to preserve keys, you can combine the first parameter with DATA_KEYS. This is useful for select, radioList or checkboxList.

getHttpData() returns sanitized input. In this case, it will always be array of valid UTF-8 strings, no matter what is sent by the form. It's an alternative to working with $_POST or $_GET directly if you want to receive safe data.

Cross-Site Request Forgery (CSRF) Protection

Nette Framework automatically protects forms in presenters against Cross-Site Request Forgery (CSRF) attacks. An attacker lures a victim on a webpage, which quietly performs a request to the server the victim is logged into. The server would not recognize whether the user sends the request willingly.

Therefore, Nette prevents the form from being submitted from another domain. To turn off protection, use:


To protect stand-alone forms, start a session via Session::start() and call the method:


Forms in Presenters

When using forms in presenters, we use class Nette\Application\UI\Form which is an extension of Nette\Forms\Form.

Using One Form in Multiple Presenters

In a case when we need to use one form in multiple presenters, we have two options:

  1. either putting it into the presenters' hierarchy into a common ancestor and define a factory there
  2. or to define it in a separate factory class and create its instance in the presenters' factories.

The appropriate place for such class is e.g. app/Forms/SignInFormFactory.php. Our factory class will look like this:

use Nette\Application\UI\Form;

class SignInFormFactory
	public function create(): Form
		$form = new Form;

		$form->addText('name', 'Name:');
		// ...
		$form->addSubmit('login', 'Log in');

		return $form;

Now in each presenters' factory, which use our form, we create a form instance using our form factory class using create() method:

protected function createComponentSignInForm(): Form
	$form = (new SignInFormFactory)->create();
	$form['login']->caption = 'Continue'; // we can also modify our form

	$form->onSuccess[] = [$this, 'signInFormSubmitted']; // and add event handlers

	return $form;

We could also process our submitted form in one place. It's as simple as moving our event handler to our factory class, renaming it from signInFormSubmitted to e.g. submitted. Alternatively, we can use an anonymous function as a handler:

use Nette\Application\UI\Form;

class SignInFormFactory
	public function create(): Form
		$form = new Form;

		$form->addText('name', 'Name:');
		$form->addSubmit('login', 'Log in');

		$form->onSuccess[] = function (Form $form, \stdClass $values): void {
			// we process our submitted form here

		return $form;

Submitting a Form

If a form contains multiple submit buttons we need to distinguish, it is useful to bind the handlers to onClick event of the button, which is invoked immediately before onSuccess.

$form->addSubmit('login', 'Log in')
	->onClick[] = [$this, 'signInFormSubmitted'];

If a form is submitted by pressing enter key, the first submit button is invoked.

Handlers of onSuccess and onClick events are invoked only if the submitted values pass validation. You don't need to validate the input inside the handler functions. The form also has onSubmit event, which is invoked irrespectively of the validation.

Sometimes can be useful to reset the form to its state before submitting. It can be done by invoking reset() method on the form:

$form->isSubmitted(); // true
$form->reset(); // the form is now in the state as it was never submitted
$form->isSubmitted(); // false

Standalone Forms

Nette Forms can be used without the whole framework as a standalone package. The form is created easily like this:

use Nette\Forms\Form;

$form = new Form;

$form->addText('name', 'Name:');
$form->addPassword('password', 'Password:');
$form->addSubmit('send', 'Sign up');

echo $form; // renders the form

Such form is submitted through POST method to the same page. You can alter that easily:

$form = new Form;

We can find out whether the form was submitted and passed validation by calling $form->isSuccess(). If so, let's print out the data.

if ($form->isSuccess()) {
	echo 'Form was filled and submitted successfully';

	$values = $form->getValues();

You can access form items like you access array. In our case, you can find the first text input on index $form['name'].

It's recommended to redirect to another page after you have processed the data. You can avoid duplicate form submission this way.

Rendering the Form

Each element has getLabel() and getControl() methods which return HTML code of label and the element itself. Nette provides getter and setter property access as if you are accessing the attribute itself.

<?php $form->render('begin') ?>
<?php $form->render('errors') ?>

<tr class="required">
	<th><?php echo $form['name']->label // Calls getLabel() ?></th>
	<td><?php echo $form['name']->control // Calls getControl()  ?></td>

<tr class="required">
	<th><?php echo $form['age']->label ?></th>
	<td><?php echo $form['age']->control ?></td>



<?php $form->render('end') ?>