# Introduction

NeatForms is a modular/extensible PHP Form Library that ships with many features, including built-in support for Bootstrap and Foundation CSS.


  • Includes and is built on Neat HTML (also available standalone).
  • Supports procedural Form Generation.
  • Supports object-oriented and serializable forms management.
  • Server-side validation of form data.
  • Client-side validation using JavaScript.
  • Supports the Bootstrap 4, Bootstrap 5 Beta and Foundation frameworks.
  • Clean, well-documented code.


NeatForms can (soon!) be installed via the Composer package manager or by using the included NeatFormsAutoloader class.


NeatForms requires that your web server runs PHP version 7.4 or greater.

Installing Using the Autoloader

NeatForms includes a simple autoloader, allowing you to use NeatForms (and NeatHTML) by including one file.
Follow the steps below for how to do this.

Assuming your application's directory structure is something like this:

  • images/
  • css/
  • index.php
Step 1: Where to put the NeatForms files

Your application may already have a libraries or vendor directory, in which case you should probably put NeatForms there, if not, simply create a libraries directory.

Copy the NeatForms and NeatHTML folders as well as the NeatFormsAutoloader.php file into your libraries ("libs") folder:

  • images/
  • css/
  • libraries
    • NeatForms
      • src
    • NeatHTML
      • src
    • NeatFormsAutoloader.php
  • index.php
Step 2: Using the NeatFormsAutoloader

Now, in any area of your application where you will be using NeatForms, you will need to include the NeatFormsAutoloader.php file and register the autoloader and pass it the path to the folder where the NeatForms and NeatHTML folders exist.

For example, in an index.php file, you can do this:
require_once 'libs/NeatFormsAutoloader.php';
NeatFormsAutoloader::register(__DIR__ .'/libraries');

// You can now begin to use NeatForms
$form = ThowsenMedia\NeatForms\NeatForms::make('POST');

Running The Example Files

Included in NeatForms is a directory with many example files which you can play around with. To run them, point your browser to where you have installed NeatForms, for example: mysite.test/libraries/NeatForms/examples

# Tutorial

Follow this tutorial for a quick introduction on NeatForms.

# Your First Form

//import the NeatForms class
use ThowsenMedia\NeatForms\NeatForms;

echo NeatForms::open('POST');
echo NeatForms::text('username');
echo NeatForms::submit('submit');
echo NeatForms::close();
<form method="POST">
    <input type="text" name="username">
    <input type="submit" value="submit">

The NeatForms class is the main access point. It has only static members, and is not meant to be instantiated.

The open method creates a new ThowsenMedia\NeatForms\Form\Form object, and can be manipulated in various ways. In this example however, we simply echo it immediately, which will automatically call its render method (which returns the HTML as a string). We do the same for the text field and the submit field. At the end we call the close method which closes the form.

The NeatForms class has static methods for creating text fields, select fields, checkbox fields, buttons etc.
Each of these methods return FormFeld objects, which has many methods for configuring the fields, such as labels, help texts, toggling autofocus etc.

# Configuring Forms

Calling NeatForms::text() or any of the other field methods will return a FormField object, which can be customized further, using chainable method calls.

The methods are chainable because they tend to return the $this instance.

Here's how to turn on autofocus for a field, for example:

echo NeatForms::text('username')
<input type="text" name="username" autofocus>

# OOP Form Creation

Forms can also be created in an object-oriented way, by calling NeatForms::make. It receives two arguments: the HTTP method to use (POST, GET etc.) and the action attribute for the form.

For example, $form = NeatForms::make('POST', '/form-action-url') creates a new NeatForms\Form\Form object which we can further configure, and add fields to.

To add fields to the form, we simply set each field as a property of the Form, like this:

$form->comment = NeatForms::textarea('comment');
You can name the properties whatever you like.

To render the form with all the fields, we can call $form->render() or we can simply echo $form because the form object implements the magic __toString method which automatically calls the render() method for us.

The __toString method is implemented by all renderable objects in NeatForms and allows for great flexibility.

Rendering The Form

//import the NeatForms class
use ThowsenMedia\NeatForms\NeatForms;

$form = NeatForms::make('POST', '/comment.php');

$form->comment = NeatForms::textarea('comment')

$form->submit = NeatForms::submit('submit');

echo $form;
<form method="POST" action="/comment.php">
  <textarea name="comment" placeholder="Comment"></textarea>
  <input name="submit" type="submit" value="Submit">

If you want to render the individual form fields yourself (perhaps in a "template" or "view"), or you want a very customized layout, you can do so by simply getting the fields from the form object. For example, like this:

$form = NeatForms::make('POST');
$form->email = NeatForms::email('email');
$form->password = NeatForms::password('password');

<form method="POST">
    <div class="custom">
        Email: <?php echo $form->email; ?>
    <div class="custom">
        Password: <?php echo $form->password; ?>

# Rendering Engines

So far, we have been rendering forms without the use of a RenderEngine. NeatForms comes with support for many CSS frameworks, but also has a Basic RenderEngine that is framework-agnostic - which is what we'll look at next.

To setup a RenderEngine, we create a new RenderEngine of our choice and pass it to the NeatForms::registerRenderEngine method.

Using the Basic Rendering Engine

First, create a new instance of the ThowsenMedia\NeatForms\Rendering\BasicRenderEngine class.
Pass true as the first argument to set it to apply some basic css classes to our form - making it easier to apply CSS styles.

use ThowsenMedia\NeatForms\NeatForms;
use ThowsenMedia\NeatForms\Rendering\BasicRenderEngine;

NeatForms::registerRenderEngine('basic', new BasicRenderEngine(true));

Here's a Full Example

use ThowsenMedia\NeatForms\NeatForms;
use ThowsenMedia\NeatForms\Rendering\BasicRenderEngine;

NeatForms::registerRenderEngine('basic', new BasicRenderEngine(true));

$form = NeatForms::make('POST', '/comment.php');

$form->comment = NeatForms::textarea('comment')
    ->help("Write your comment here.");

$form->submit = NeatForms::submit('submit');

echo $form;
<form method="POST" action="/comment.php">
    <div class="neatforms--field">
        <div class="neatforms--field-label">
            <label for="comment">Comment</label>
        <textarea class="neatforms--field-control" name="comment" placeholder="Comment"></textarea>
        <div class="neatforms--field-help">
            Write your comment here.

    <div class="neatforms--field">
        <input class="neatforms--field-control" name="submit" type="submit" value="Submit">

The BasicRenderEngine wraps all fields in a div with a class neatforms--field, and can render the labels and help blocks.

As you might notice, we're calling the help method on the TextArea field to set the help text. This is rendered below the field and can be styled by applying CSS to the .neatforms--field-help CSS class.

# Validation

The BasicRenderEngine supports the display of validation errors - let's take a quick look at how to add validation and how to process that on the server-side in PHP.

Before we begin adding validation rules to our forms, we need to register some Validators.
Luckily, NeatForms comes pre-packaged with many Validators for minimum and maximum length of strings, required fields, numbers, emails, etc.

Registering Validators

To register the included validators, call the register method on the BasicValidatorsProvider.

use ThowsenMedia\NeatForms\Validation\BasicValidatorsProvider;

See Validation for more information, including how to add your own validators.

Adding validation rules on Forms.

Validation rules can be applied to each form field, by calling the rules method on the FormField object.
The rules method takes a single array of strings, where each string contains the validator type, and an optional list of parameters, formatted this way:

  'required' // makes the field required
  'min:18' // must be at least 18
  'alpha' // alphabetic characters only


form = NeatForms::make('POST');
$form->username = NeatForms::text('username')
$form->password = NeatForms::password('password')
$form->signup = NeatForms::submit('Sign Up');

Here, we added:

  • The required validator to the username field
  • The required validator to the password field
  • The minlength validator to the password field, with the length parameter set to 8. This makes sure passwords are long enough (for security... presumably!)

Processing & Validating Form Requests

Finally, it's time to process those forms! the ThowsenMedia\NeatForms\Form\Form object has a wasPosted method, which checks if the $_REQUEST contains data, and that the HTTP Request was sent with the proper method (I.E, 'GET' or 'POST').

For example:

If the $form->wasPosted() return true, we do something with the data (send an email, log in a user etc), and then we might redirect or just show a message.
If it returns false, we might display an error message and render the form.

Using the Validator Class:

Since we have some rules on our fields, we can use the Validator class.
We can get a Validator object, with the rules from our form, by calling $form->getValidator()
Then, we can call $validator->validate and pass it the input.

For example:
$form = NeatForms::make('POST');
$form->username = NeatForms::text('username')
$form->password = NeatForms::password('password');
$form->login = NeatForms::submit('Login');

if ($form->wasPosted()) {
    //process the form data, try to log in etc.
    echo 'Success!';
}else {
    echo 'Something went wrong, please try again:';
    echo $form;
//after creating the form...
if ($form->wasPosted()) {
    // getValidator creates a new FormValidator instance, with the rules from our form object.
    $validator = $form->getValidator();

    // $_POST contains form input using the "POST" HTTP method.
    // use $_REQUEST to get input from any HTTP method.
    if ($validator->validate($_POST))
        // our form data is valid, do something here...

        echo 'Everything is good!';
    }else {
        echo $form->withErrors( $validator->getErrors() );
}else {
    //nothing was posted, just display the form.
    echo $form;

If the $validator->validate() call returns false, we echo the form but pass in an array of errors, which we can get from the validator by calling the $validator->getErrors() method.

When rendering the form again after the user has submitted it, because it was not successful. Its useful to fill the form with the previously submitted data.
This can be done by calling $form->fill($_POST) when rendering it:

if ($validator->validate($_POST))
    // our form data is valid, do something here...

    echo 'Everything is good!';
}else {
    echo $form
        ->withErrors( $validator->getErrors() );

# Form Fields

NeatForms support the creation of any HTML5 Form Field element, in addition to some custom wrappers for layout purposes, such as the row() and fieldset() methods.

Here's a list of all the static field creation methods on the NeatForms class:

- These methods return either a InputField instance, or an instance of a class inheriting from the FormField class.
- [these] are option arguments.
- Some of the fields have their own methods for customizing the field. These are noted below, as well.

  • text(name, [help], [label]): InputField
  • password(name, [help], [label]): InputField
  • textarea(name, [help], [label]): TextArea
  • number(name, [help], [label]): InputField
  • range(name, [min], [max], [help], [label]): RangeField
    • ->setMin(min): $this // set the min value or pass null to remove it.
    • ->getMin(): float|null // returns the minimum value, or null if not set.
    • ->setMax(max): $this // set the max value or pass null to remove it.
    • ->getMax(): float|null // returns the maximum value, or null if not set.
  • file(name, [help], [label]): FileField
    • ->accept(['.filetype', '.png']) Sets the accepted file types for this field. An empty array would allow all file types.
    • ->multiple(true|false) enable or disable multiple file uploads.
  • select(name, array:options = []): Select
    • ->options(array:options = []) set the options array
  • tel(): InputField
  • url(): InputField
  • date(): InputField
  • time(): InputField
  • week(): InputField
  • month(): InputField
  • color(): InputField
  • checkbox(): CheckboxList
  • radio(): RadioList
  • checkboxList(): CheckboxList
  • radioList(): RadioList
  • fieldset(): Fieldset
  • row(): FormRow

# The HTML Element

NeatForms is built on a simple HTML Library, which has an Element class.

The FormField class (which all form fields extend from), and the Form class, have an $element property, which is the underlying HTML element.

The Element class has useful methods for setting attributes, with special handling for setting/removing CSS classes.

In addition, the element class can contain child elements as well. This is implemented via ArrayAccess, as well as the __get and __set methods.

Here's how to get the underlying element of any FormFIeld instance and set the title attribute:

$form = NeatForms::make();
$form->username = NeatForms::text('username');

// here we set the title attribute on the field element - this is the field "control", I.E, the <input> element - not a wrapper element.
$form->username->element->attr('title', 'This is the title that appears in a popup when you hover over the field');

Adding a class:


You can set multiple classes as well, using either an array or a space-delimited string:

$form->username->addClass(['my-second-class', 'my-third-class']);
$form->username->addClass('my-fourth-class my-fifth-class');

You can do the same for the Form class:

$form = NeatForms::make();

There's also:

  • getClasses which returns all classes as an array of strings,
  • hasClass which checks if the current class exists.
  • removeClass which removes a class.

# Handling File Uploads

As of version 3.4.0, NeatForms comes with extra features to make it easier to handle File Uploads.

In order to accept file uploads, the <form> element needs to have the enctype attribute set to "multipart/form-data". I.E <form enctype="multipart/form-data">. This is automatically applied when adding a FileField to a form.

There's also a UploadedFile class which makes it easier to process the files.

Please see the example file at examples/fileuploads/basic.php for a working example.

Creating a file field

To create a file field, simply call NeatForms::file():

$fileField = NeatForms::file('photo');

To apply a maximum file size validation rule, use the file-max-size validator type, with the argument in the form of a number, followed by either kb for kilobytes, mb for megabytes, or gb for gigabytes.

For example, to set the maximum size to 2.5 megabytes:


Moving The Uploaded File

Normally, when you want to process the uploaded file, you access the $_FILES superglobal. With NeatForms, instead we do $form->getUploadedFile('photo').

We can then easily move the uploaded file to any directory we want, and optionally rename the file.

We also have access to the file size, in bytes, kilobytes, megabytes or gigabytes:

if ($form->wasPosted()) {
  $photo = $form->getUploadedFile('photo');
  $photo->save('/path/to/uploads_directory', 'user_24_profile_picture.png');

# Validation

NeatForms supports form validation within PHP as well as on the client using native HTMl5 validation, and via an included jQuery plugin.

# Included Validators

The following validators are provided through the BasicValidatorsProvider:

Name Description
required Makes the field required (fails if no value is passed)
required-with Makes the field required when another field has a value. For example: required-with:username would make this field required, if the uesrname field has a value.
minlength Sets a minimum character length. For example: minlength:3
maxlength Sets a maximum character length. For example: maxlength: 255
min Sets a required minimum number. For example: min: 18
max Sets a maximum allowed number. For example: max: 99
email Requires that the value is a valid E-Mail address. Uses the built-in PHP Validator.
alpha Accepts alphabetic characters only.
alpha-dash Accepts alphabetic characters and dashes.
alpha-numeric Accepts alphabetic characters and numbers.
alpha-numeric-dash Accepts alphabetic characters, numbers and dashes.
date Accepts strings that when passed to the strtotime PHP function, returns a valid DateTime object.
file-max-size Sets a maximum file size for file uploads. For example: max-file-size:2.5mb (you can use kb, mb, and gb)

See Tutorial > Validation for how to use these.

# Custom Validators

To create a custom validator, you need to create a class that implements the ValidatorTypeInterface.
Then, call the static registerValidatorType method on the FormValidator class and pass it your validator class.

Make sure to pass it the class and not an instance of it.
To get the class reference, you can call MyValidatorClass::class.

Here's an example, taken from the BasicValidatorsProvider class which is included in NeatForms:

use ThowsenMedia\NeatForms\Validation\FormValidator;

FormValidator::registerValidatorType('required', new class implements ValidatorTypeInterface {
    public function setupForField(FormField $field, array $parameters)
        $field->element->setAttribute('required', '');

    public function validate(array $input, string $field, $value, array $parameters): bool
        if (is_string($value)) return strlen($value) > 0;
        return $value ? true : false;

    public function getError(array $input, string $field, $value, array $parameters): string
        return "$field is required.";

Note that, in the above example, instead of defining the class first and then passing it he class reference, we just pass in an anonymous class.

# Rendering

Forms are by default rendered very simply, without labels, help blocks or validation messages.
When a RenderEngine is added to NeatForms, the form is displayed by the renderer.

The RenderEngineInterface

A Render Engine is a class that implements the ThowsenMedia\NeatForms\Rendering\RenderEngineInterface.

The included Render Engines are:

  • BasicRenderEngine
  • BasicTableRenderEngine
  • Bootstrap4RenderEngine
  • Bootstrap5RenderEngine (which extends from the Bootstrap4RenderEngine)

Using a RenderEngine

To use a RenderEngine, we need to create an instance of it, and tell NeatForms about it by calling theNeatForms::registerRenderEngine method.

You pass it a name and the instance of the engine, like so:

use ThowsenMedia\NeatForms\NeatForms;

use ThowsenMedia\NeatForms\Bootstrap4RenderEngine;

NeatForms::registerRenderEngine('bootstrap', new Bootstrap4RenderEngine());

NeatForms was configured to allow multiple engines to be used at the same time, therefore we need to give them an identifier. This is just a string.
NeatForms is configured to fall back to a default render engine if multiple engines are added.
The first engine to be registered will be set as the new default engine.

# The BasicRenderEngine

The BasicRenderEngine wraps all fields in a parent div element, and can render labels, validation errors, and help blocks.

When creating the BasicRenderEngine, you may pass true as the first argument to have it add a variety of css classes to the html output. This way, you can get started by writing some CSS for it right away.


use ThowsenMedia\NeatForms\NeatForms;
use ThowsenMedia\NeatForms\Rendering\BasicRenderEngine;

// require, or autoload the files...

// here, we pass true as the argument to the BasicRenderEngine cosntructor, which tells it to add styling classes.
NeatForms::registerRenderEngine('basic', new BasicRenderEngine(true));

These CSS classes, like .neatforms--field, .neatforms--field-control, .neatforms--field-label etc. Can be used to add your own styling, or use the included CSS.

Using the included CSS

NeatForms comes with two CSS files (located in the CSS folder):

  1. neatforms-basic.css
  2. neatforms-extra.css

neatforms-basic.css adds very minimal styling–mostly layout, spacing and some basic validation state coloring.

neatforms-extra.css can be used together with neatforms-basic.css and it adds some more visually appealing styling.

Try them out:

  <link rel="stylesheet" src="CSS/neatforms-basic.css">
  <link rel="stylesheet" src="CSS/neatforms-extra.css">

The FormRow

The BasicRenderEngine comes with its own FormRow class, and a macro on the NeatForms class, called row which creates a new row.

The FormRow lets you create a row of horizontally aligned form elements.

Here's how:

$form->name = NeatForms::row([
  'first_name' => NeatForms::text('first_name'),
   'last_name' => NeatForms::text('last_name')

Alternatively, you can also do it this way:

$form->name = NeatForms::row();
$form->name->first_name = NeatForms::text('first_name');
$form->name->last_name = NeatForms::text('last_name');

# Bootstrap 4

This engine wraps the fields in proper elements with the CSS classes that bootstrap expects.
In addition, it dynamically adds a few new methods on all FormField objects:

  • bs_prepend() which prepends some text before a field.
  • bs_append() which appends text after a field.
  • bs_size() which takes a string argument, either 'sm' or 'lg' ('small' or 'large' works too). This sets the form-control-sm or form-control-lg classes on the fields.

The bootstrap 5 RenderEngine is pretty much like the bootstrap4 Engine.

# Bootstrap 5

The Bootstrap5RenderEngine is pretty much like the Bootstrap4RenderEngine.

# Custom Render Engines

To add your own custom RenderEngine, you need to make a class that implements the ThowsenMedia\NeatForms\Rendering\RenderEngineInterface.

I advice you to look at the ThowsenMedia\NeatForms\Rendering\BasicRenderEngine file and maybe copy over the code from that and modify it as you need.

Then, simply register it and it should work.

# BasicTableRenderEngine

The ThowsenMedia\NeatForms\Rendering\BasicTableRenderEngine renderer can render labels and fields horizontally. It extends from the BasicRenderEngine, and wraps the output in rows and two columns for the label and field control.

See example examples/example-basictable.php.

# jQuery & AJAX

NeatForms comes with an included plugin for jQuery which supports conditional fields and of course AJAX form submission.

Please see the "example09-jquery.php" example file, included in the examples directory of NeatForms for a working example.

# jQuery Plugin

The plugin file is called NeatForms.js and is in the JS folder of NeatForms.
Make sure you include the jQuery library before including the plugin.

Step 1: include NeatForms.js and a special NeatForms::getJS() call at the end of your body.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="/libraries/NeatForms/JS/NeatForms.js" type="text/javascript">
    <?php echo NeatForms::getJS(); ?>

The NeatForms::getJS() method prints out dynamically generated javascript which will initialize the plugin for your forms.

Step 2: Setting up AJAX on the PHP side.

$form = NeatForms::make('POST');
$form->comment = NeatForms::textarea('comment')
        'required', 'maxlength:255',    

if ($form->wasPosted()) {

    $validator = $form->getValidator();
    if ($validator->validate($_POST)) {
        echo $form->ajaxResponse("Success!");
    }else {
        echo $form->ajaxResponse("Invalid form data. Please check")

}else {
    echo $form;
  • We call $form->setAJAX(true) which will specify that this form uses the NeatForms.js Plugin.
  • We use the ajaxResponse() method to send the response to the client, this is atomatically formatted to JSON.

# Advanced

NeatForms strives to be easy to learn, but powerful. Here are some of the more advanced features.

# Reusable Forms

For larger projects, it can be useful to store all forms as individual files, in a central "forms" directory.
NeatForms supports this via the NeatForms::load method - however, you must first assign a FormLoader.

To use a FormLoader, you can use the included ThowsenMedia\NeatForms\Loading\SimpleFormLoader class, or create your own.

Using the SimpleFormLoader

The built-in SimpleFormLoader maps form "ids" or "names" to a php file of the same name, in PascalCase, containing a class of the same name, plus "Form" at the end.

Step 1: Forms directory

Create a folder, for example my_project/forms

Step 2: A SavedForm class

Create a php file, for example: my_project/forms/ContactForm.php

In that file, define the your form as a class that extends from the SavedForm class:

 * my_project/forms/ContactForm.php

class ContactForm extends ThowsenMedia\Neatforms\SavedForm

    public function setup()
        $this->name = NeatForms::text('name', null, 'Your Name');
        $this->email = NeatForms::email('email', null, 'Your E-Mail');
        $this->message = NeatForms::textarea('message', null, 'Your Message');

        $this->submit = NeatForms::submit('Submit');


Step 4: The FormLoader

Now that we have a forms directory with a form, we can create a SimpleFormLoader instance, and assign it to NeatForms:

use ThowsenMedia\NeatForms\Neatforms;
use ThowsenMedia\NeatForms\Loading\SimpleFormLoader;

# create our SimpleFormLoader to map form id's to form files and classes.
$formLoader = new SimpleFormLoader("my_project/forms");


Step 5: Loading

Now, you can load your contact form any time you need it:

contactForm = NeatForms::load('contact');

# Serialization

Forms can be serialized into text, stored in, for example a database, and then unserialized back into a form object.

To serialize a form, call the serialize method on the form:

$form = NeatForms::make('POST');

// this returns a string that can be stored.
$serialized = $form->serialize();

With that serialized string, you can store it anywhere, and then unserialize it when you want to use it again:

$form = unserialize($serialized);

//you can now work with the form object and do $form->render() etc.