diff --git a/README.md b/README.md
index 519d15f6126e43583a27d05b0e777b8feaf9fbf6..c129e94afbe18c2c8ba172848d73a8b89bec7bf3 100644
--- a/README.md
+++ b/README.md
@@ -50,11 +50,11 @@ interface SettingsTab {
 	/**
 	 * Use to set settings from database or defaults.
 	 *
-	 * @param array|ContainerInterface $data Data to render.
+	 * @param ContainerInterface $data Data to render.
 	 *
 	 * @return void
 	 */
-	public function set_data( $data );
+	public function set_data( ContainerInterface $data );
 
 	/**
 	 * Use to handle request data from POST.
@@ -102,11 +102,11 @@ abstract class FieldSettingsTab implements SettingsTab {
 		return $this->get_form()->render_form( $renderer );
 	}
 
-	public function set_data( $data ) {
+	public function set_data( ContainerInterface $data ) {
 		$this->get_form()->set_data( $data );
 	}
 
-	public function handle_request( $request ) {
+	public function handle_request( array $request ) {
 		$this->get_form()->handle_request( $request );
 	}
 
diff --git a/changelog.txt b/changelog.txt
index 10aef4ca0341dbc0807abb45731357d7bb41b917..c9aac5b582038127ee3cfe66ea74a1a10d9ce742 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,11 +1,17 @@
 # Changelog
 
-## [3.0.0] - 2021-09-08
+## [3.0.0]
 ### Added
 - Added strong typing to all of the interfaces
 - Added fields sorting by priority field
-###
+- Normalized string escaping in all template files
+- Added InputEmailField and EmailSerializer classes
+### Changed
+- All getters and setter in BasicField are now declared final
+- FormWithFields accepts only ContainerInterface in ::set_data() method. Prepare data before passing it
+### Removed
 - Removed deprecated classes
+- Removed NoSerialize class
 
 ## [2.4.6] - 2021-08-23
 ### Fixed
diff --git a/src/Field.php b/src/Field.php
index 311d8eb2474614314d16e3d948fad8e74e86b427..08329e7f0a4166ed8d3551075b60117770dae779 100644
--- a/src/Field.php
+++ b/src/Field.php
@@ -91,5 +91,7 @@ interface Field {
 
 	public function get_serializer(): Serializer;
 
+	public function has_serializer(): bool;
+
 	public function get_priority(): int;
 }
diff --git a/src/Field/BasicField.php b/src/Field/BasicField.php
index 8e1a7e000938fad7154f3bc485e4dd6fa1e5f961..4c1dc0cf40c58b7eb0291921b66f1260f5c2b9f7 100644
--- a/src/Field/BasicField.php
+++ b/src/Field/BasicField.php
@@ -21,7 +21,7 @@ abstract class BasicField implements Field {
 
 	const DEFAULT_PRIORITY = 10;
 
-	/** @var array{default_value: string, possible_values?: string[], sublabel?: string, priority: int, label: string, description: string, description_tip: string, data: array<string|int>, serializer: ?Serializer} */
+	/** @var array{default_value: string, possible_values?: string[], sublabel?: string, priority: int, label: string, description: string, description_tip: string, data: array<string|int>} */
 	protected $meta = [
 		'priority'          => self::DEFAULT_PRIORITY,
 		'default_value'     => '',
@@ -29,7 +29,6 @@ abstract class BasicField implements Field {
 		'description'       => '',
 		'description_tip'   => '',
 		'data'              => [],
-		'serializer'        => null,
 	];
 
 	public function should_override_form_template(): bool {
@@ -53,8 +52,12 @@ abstract class BasicField implements Field {
 		return new NoSanitize();
 	}
 
+	public function has_serializer(): bool {
+		return false;
+	}
+
 	public function get_serializer(): Serializer {
-		return null;
+		throw new \BadMethodCallException('You must define your serializer in a child class.');
 	}
 
 	final public function get_name(): string {
@@ -231,10 +234,6 @@ abstract class BasicField implements Field {
 		return $this->attributes['required'];
 	}
 
-	final public function has_serializer(): bool {
-		return ! empty( $this->meta['serializer'] );
-	}
-
 	final public function get_priority(): int {
 		return $this->meta['priority'];
 	}
diff --git a/src/Field/ProductSelect.php b/src/Field/ProductSelect.php
index 2a10e467a6392525b113efa0bd42202be852755b..8d8b5e63620d46d216ce67cede2f1477f02809b4 100644
--- a/src/Field/ProductSelect.php
+++ b/src/Field/ProductSelect.php
@@ -2,11 +2,22 @@
 
 namespace WPDesk\Forms\Field;
 
+use WPDesk\Forms\Serializer\ProductSelectSerializer;
+use WPDesk\Forms\Serializer;
+
 class ProductSelect extends SelectField {
 	public function __construct() {
 		$this->set_multiple();
 	}
 
+	public function has_serializer(): bool {
+		return true;
+	}
+
+	public function get_serializer(): Serializer {
+		return new ProductSelectSerializer();
+	}
+
 	public function get_template_name(): string {
 		return 'product-select';
 	}
diff --git a/src/Field/TimepickerField.php b/src/Field/TimepickerField.php
index 84682545bd7be8f4dfcb00cb3c78f5eed86762e0..c7c1a5d12ad2c222720f2ce62392151c71fe5590 100644
--- a/src/Field/TimepickerField.php
+++ b/src/Field/TimepickerField.php
@@ -2,7 +2,18 @@
 
 namespace WPDesk\Forms\Field;
 
+use WPDesk\Forms\Serializer;
+use WPDesk\Forms\Serializer\JsonSerializer;
+
 class TimepickerField extends BasicField {
+	public function has_serializer(): bool {
+		return true;
+	}
+
+	public function get_serializer(): Serializer {
+		return new JsonSerializer();
+	}
+
 	public function get_template_name(): string {
 		return 'timepicker';
 	}
diff --git a/src/Persistence/FieldPersistenceStrategy.php b/src/Persistence/FieldPersistenceStrategy.php
index 7a70cd5e429e9b8e3e9545a9af85fe951fd4aabd..3102f1ce6f69d8f9dc6f790a9a885e92076d3e39 100644
--- a/src/Persistence/FieldPersistenceStrategy.php
+++ b/src/Persistence/FieldPersistenceStrategy.php
@@ -20,9 +20,7 @@ class FieldPersistenceStrategy {
 		$this->persistence = $persistence;
 	}
 
-	/**
-	 * Save fields data.
-	 */
+	/** @return void */
 	public function persist_fields( FieldProvider $fields_provider, array $data ) {
 		foreach ( $fields_provider->get_fields() as $field ) {
 			$field_key = $field->get_name();
@@ -34,9 +32,7 @@ class FieldPersistenceStrategy {
 		}
 	}
 
-	/**
-	 * Load fields data.
-	 */
+	/** @return void */
 	public function load_fields( FieldProvider $fields_provider ): array {
 		$data = [];
 		foreach ( $fields_provider->get_fields() as $field ) {