---
title: '@encoder / @decoder'
description: Setup encoding and decoding for custom data types.
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jaspr.site/llms.txt
> Use this file to discover all available pages before exploring further.

---

With the `@encoder` and `@decoder` annotations you can set up custom serialization for any type. This is used
by Jaspr for both:

- (de-)serializing parameters of [@client](/api/utils/at_client) components
- (de-)serializing fields using the [@sync](/api/utils/at_sync) annotation

## Usage

To enable custom serialization for your data class use the annotations in the following way:

- The `@decoder` annotation must be used on a **constructor** or **static method** that returns an instance of the class
  and has exactly one parameter of any primitive serializable type (`bool`, `int`, `String`, etc.).

- The `@encoder` annotation must be used on an **instance method** that returns the same type that the
  `@decoder` method takes in.

- Both methods can have any name.

In practice this can look like this:

```dart title="model.dart"
class Model {
  // Constructors, fields, etc.
  /* ... */

  @decoder
  static Model decode(Map<String, dynamic> data) => /* ... */;

  @encoder
  Map<String, dynamic> encode() => /* ... */;
}
```

<Info>
The annotations are designed to be compatible with virtually any serialization package like `json_serializable`
or `dart_mappable`. Simply annotate the methods generated by these packages either directly, or by using a wrapper method.
</Info>

While `Map<String, dynamic>` is the most common type for serializing Dart classes, you can also use other primitive types
like `String`, `List<dynamic>` etc. as long as the encoding method returns the same type as the decoding method accepts.

An enum can use the annotations the same way, for example to serialize its values by name:

```dart title="color.dart"
enum Color {
  red,
  green;

  @decoder
  static Color fromName(String name) => Color.values.byName(name);

  @encoder
  String toName() => name;
}
```

## Customize serialization of third-party types

Sometimes, you cannot control the methods of a class, such as when using a third-party library.
In these cases, you can create a custom override type which "redefines" the type for serialization.

For example, consider the case where the `Model` class is imported from a third-party library, `package:model`.
Since we do not own `package:model`, we cannot change the methods of the `Model` class.

Instead of modifying the `Model` class, we can define a custom override for `Model` which will be used in place of the
original `Model` class when it's used for serialization in Jaspr.

```dart title="lib/model_override.dart"
import 'package:jaspr/jaspr.dart';
import 'package:model/model.dart';

// We create an extension type which wraps over the third-party type and
// defines the @decoder and @encoder methods
//
// Since the extension type implements the original type, it will behave
// identically to the original except for the serialization logic.
extension type ModelOverride(Model model) implements Model {
  @decoder
  factory ModelOverride.decode(Map<String, dynamic> data) {
    return ModelOverride(
      Model(
        // decode the values of [data]
        /* ... */
      ),
    );
  }

  @encoder
  Map<String, dynamic> encode() => {
      // encode the values of [model]
      /* ... */
    };
}
```

Custom overrides are defined as [extension types](https://dart.dev/language/extension-types) which wrap and implement
the original type. You must always implement the original type in your extension type.
