# Nette Forms writes the client and server validation for you, from one rule set

> Nette Forms is a PHP library for building, rendering and processing web forms, with validation rules that run in the browser and on the server from the same declaration. It removes the routine work of maintaining two validation scripts, and it ties you to a recent PHP version and a specific rendering style.

**nette/forms** — 📝 Generating, validating and processing secure forms in PHP. Handy API, fully customizable, server & client side validation and mature design.

- Repository: https://github.com/nette/forms
- Website: https://doc.nette.org/forms
- Stars: 531 · Forks: 145
- Language: PHP
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/nette-forms

## One rule set, two places it runs

The usual cost of a web form is not the markup, it is the second copy of the validation. You write rules in JavaScript so the user gets feedback without a page reload, and you write them again in PHP so the server does not trust the browser. The two copies drift, and the drift is where the bugs live.

Nette\Forms removes that duplication by generating both sides from one declaration. The library validates sent data both client-side, in JavaScript, and server-side, and it does so dynamically and transparently, with nothing set manually. The README lists the capabilities as client and server validation, a high level of security, multiple render modes, and translations and i18n.

The framing of the project is worth taking seriously because it sets expectations. The documentation asks why you would set up a framework for a simple web form, and answers that you will not have to take care of routine tasks such as writing two validation scripts, client and server, and that your code will be safe against security breaches. That is a claim about maintenance burden rather than about features, and it is the burden that disappears in practice. A form whose rules live in one place can also be re-used between a registration form and a settings form without a copy and paste, which is a bigger win over time than the first hour saved.

## What gets filtered before your handler runs

Forms are the most common user input, which is the reasoning the README gives for putting so much defensive work into the library. Cross Site Scripting and Cross Site Request Forgery are filtered, as are special control characters, all inputs are checked for UTF-8 validity, and every multiple-choice, select box and similar control is checked for forged values at validation time. The forged value check is the one worth understanding, because it closes a hole that hand written validation usually leaves open: a select box has a fixed set of legal options, and a client that posts something else is asking to be trusted.

The README asserts that Nette Framework puts a great effort to be safe and that Nette forms are as good as impenetrable. That is the author's phrasing, and it is not a security guarantee you should cite in your own documentation. What you can rely on is the list of behaviours above, which are specific and checkable, and the fact that the project treats input filtering as part of the default path rather than as something you opt into.

The practical question for an adopter is where the boundary sits. These are the checks the library performs on its own. Anything beyond them, such as business rules about what a user is allowed to do with a submitted form, is still your code.

## Declaring the form is four method calls

The API is small enough to show in full. A registration form in the README is declared like this:

```php
use Nette\Forms\Form;

$form = new Form;

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

echo $form; // renders the form
```

Each control is added with a name and a label, and echoing the form object renders it. Validation is added on top with a method call, where the optional argument is the error message shown when the field is empty:

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

That chaining style is the shape of the whole library. Rules attach to the control they belong to, so a validator for one field cannot be silently applied to another, and a form is readable in the order the fields appear on the page.

The repository's `examples/` directory shows how much variation the same declaration supports. There are examples for Bootstrap 4 and Bootstrap 5 rendering, HTML5 specific markup, custom controls, custom rendering, custom validators, manual rendering, containers, live validation, a Latte example, and localisation through both a PHP file and an `.ini` file. The presence of a manual rendering example matters for anyone with an existing template setup, since it is the difference between the library owning the markup and the library handing you elements to place yourself.

## The browser half is a separate npm package you have to add

The client side is not bundled with the PHP package. Client-side support is installed with npm or yarn, using the `nette-forms` package, and the file the page needs is `netteForms.js`, which sits at `/client-side/forms` in the distribution package:

```bash
npm install nette-forms
```

Including it in a page is a single tag:

```html
<script src="netteForms.js"></script>
```

The script reads the markup the server rendered and applies the same rules in the browser, so there is no second description of the rules to maintain. The one piece of visual coupling is the `required` class, which the library adds to mandatory elements, so a stylesheet can react to it. The README's example turns the label of a required input red with a small rule:

```html
<style>
.required label { color: maroon }
</style>
```

If you skip the JavaScript, the form still works and still validates, because the server side check is the one that counts. The browser half is convenience, which is also why it can be added later without a rewrite.

A detail worth planning for is the build side of the JavaScript. The `package.json` at the repository root uses a Rollup build with a terser plugin and TypeScript sources, ESLint for linting, and Vitest for tests, and the `postbuild` script runs the test suite after the build. The published asset is a bundle, not the source, so a team that wants to modify the behaviour of the client script is working against a project that expects a Node toolchain.

## Rendering modes are the reason the library has opinions

Multiple render modes is one of the four capabilities the README claims, and it is the feature most likely to shape your adoption. A form library has to render labels, inputs, error messages and the submit button, and every project has an opinion about the markup. Nette Forms ships with several modes, and the examples directory makes the alternatives visible: Bootstrap 4 and Bootstrap 5 renderings, HTML5 output, Latte integration, custom rendering and manual rendering for projects that want to place the fields themselves, plus container support for grouping related controls.

The trade is real. Out of the box you get markup that matches the library's idea of good forms, which is consistent and accessible but not necessarily your design system's. The `custom-rendering.php` and `manual-rendering.php` examples are the escape route, and they turn the library from a rendering engine into a validation engine that happens to emit HTML.

There is also a localisation angle. The README lists translations and i18n among the capabilities, and the examples include a `localization.ini` file alongside a PHP variant, so message catalogues can be external. For a product shipped in more than one language, the question to ask is not whether the library supports i18n but how the validation messages are keyed, and the documentation on the project site is where that question gets answered.

## Composer install, and a PHP floor of 8.3

Installation is one Composer command:

```bash
composer require nette/forms
```

The version requirement is the line to notice. The library requires PHP version 8.3 and supports PHP up to 8.5, so a deployment target has to sit in that window. This is a normal consequence of a library that tracks modern PHP, and it also means the upgrade path of an existing application depends on the hosting platform moving forward first.

The project is published as `nette/forms` on Packagist, with the download badge in the README pointing at the package page, and the full documentation lives at doc.nette.org/forms. The repository itself is a single library package rather than a monorepo of many, with `src/` for the code, `tests/` for the suite, `docs/`, `examples/`, a `composer.json`, a `phpstan.neon` for static analysis, and an `AGENTS.md` at the root. There is also a `.phpstorm.meta.php`, which is how the IDE learns the dynamic parts of the API, and the README's claim that everything is maintained dynamically and transparently is a reference to that kind of runtime metadata rather than to configuration you write by hand.

The source of truth for the version of the library is Composer, not the repository tags, so a project that pins `nette/forms` in `composer.json` and locks it will move on the schedule the maintainer chose rather than on the schedule of your deploy.

## New BSD in a file GitHub cannot classify

The licence story needs a careful sentence. The README badge refers to the file `license.md` in the repository root and labels it New BSD, and the badge points at the file on GitHub rather than at a standard licence name. GitHub's own classifier lists the repository's licence as a custom one that it cannot categorise, which is why the project appears in licence audits as Other rather than BSD.

That is a filing detail, not a legal one, and the text of `license.md` is what governs. The practical point for a company adopting the library is that an automated licence check will not resolve it on its own, so someone has to read the file and record the answer internally. New BSD is a permissive licence with attribution obligations, but confirming the exact wording for this project means reading `license.md` rather than assuming a stock template.

On maintenance, the release list shows a regular stream: v3.3.0 on 2026-06-24, v3.2.9 on 2026-05-26 and v3.2.8 on 2025-11-23. The last push to the repository was on 2026-08-27 and the project is not archived, so the tree is still moving. The upgrade cost for a consumer is therefore a matter of tracking patch releases rather than a migration, but the PHP version floor means the real ceiling on how long you can stay on an old release is the version of PHP your host supports.

## Conclusion

Nette Forms suits a PHP team that wants one declaration of the rules and matching client and server checks without writing both by hand, and it does not suit a project that must run on PHP older than 8.3 or that renders its forms in a house template engine rather than through the library's own renderers. Before you adopt it, read `license.md` in the repository root, because the project ships a New BSD licence text that GitHub cannot classify automatically, and confirm your deployment target is on PHP 8.3 through 8.5.

## FAQ

### What is Nette Forms and what problem does it solve?

It is a PHP library for creating, rendering and processing web forms, and it validates submitted data both client-side in JavaScript and server-side in PHP from a single set of rules. The point is to stop maintaining two validation scripts that drift apart.

### How do I install Nette Forms and its JavaScript?

The PHP side is installed with `composer require nette/forms`, which requires PHP 8.3 and supports PHP up to 8.5. The client side is a separate package installed with npm or yarn as `nette-forms`, and the page links the `netteForms.js` file found at `/client-side/forms` in the distribution package.

### What security checks does Nette Forms perform on submitted data?

According to the README, it filters Cross Site Scripting and Cross Site Request Forgery issues as well as special control characters, checks all inputs for UTF-8 validity, and validates every multiple-choice and select box value against its allowed set so forged values are rejected.

### Can I use my own markup with Nette Forms?

Yes, the examples directory includes custom-rendering and manual-rendering examples alongside the Bootstrap 4, Bootstrap 5, HTML5 and Latte renderings, and there are examples for custom controls, custom validators and containers. That lets the library act as a validation engine with your own template layer.

### What licence is Nette Forms released under?

The project ships its licence text in `license.md` in the repository root and the README badge labels it New BSD, but GitHub classifies the repository as a custom licence it cannot categorise. Read `license.md` directly, since automated licence checks will not resolve it for you.

## Sources

- [Issues](https://github.com/nette/forms/issues)
- [nette/forms on GitHub](https://github.com/nette/forms)
- [Project website](https://doc.nette.org/forms)
- [README](https://github.com/nette/forms/blob/master/README.md)
- [Releases](https://github.com/nette/forms/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nette-forms
