# Freezed: turning a Dart constructor into a full immutable model

> A build_runner code generator that writes equality, hashCode, toString, copyWith, unions and JSON serialization from a single annotated class, at the cost of a generated file you did not write.

**rrousselGit/freezed** — Code generation for immutable classes that has a simple syntax/API without compromising on the features.

- Repository: https://github.com/rrousselGit/freezed
- Website: https://pub.dev/packages/freezed
- Stars: 2,196 · Forks: 304
- Language: Dart
- License: not declared
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/rrousselgit-freezed

## The boilerplate Freezed removes, itemised

The README's motivation section is a list of things you have to do by hand in Dart to define a model:

- Define a constructor plus properties
- Override `toString`, `operator ==` and `hashCode`
- Implement a `copyWith` method to clone the object
- Handle deserialization and serialization

The claim is that this can run to hundreds of lines per model, is error-prone, and hurts readability. Each item on that list is a place where a hand-written implementation is subtly wrong in a way tests will not catch: an equality override that forgets a field, a `hashCode` that does not match `==`, a `copyWith` that aliases a nested object when you assumed it cloned.

The package positions itself in a crowded category by claiming a simple syntax and API without compromising on features. The phrase in the repository description is `yet another code generator for data classes, tagged unions, nested classes and cloning`, which is an accurate summary of the scope. The README's index confirms it: four capabilities, not one.

The comparison images the project uses are a before and after screenshot rather than a code listing, which is a fair editorial choice but means the README shows you the shape of the win without showing you the code. That gap is closed further down, where the install and generator sections give real commands.

## Three packages, one generator run

Installation is a standard build_runner setup, and the README is explicit that three packages are involved rather than one:

```console
dart pub add \
  dev:build_runner \
  freezed_annotation \
  dev:freezed
```

`build_runner` is the tool that runs code generators, `freezed` is the generator itself and belongs in dev dependencies, and `freezed_annotation` holds the annotations you import in your source and therefore belongs in regular dependencies. That split is the standard Dart convention for annotation-based generators, and getting it wrong produces a package that fails to build for consumers.

The same command works with `flutter pub add` for a Flutter project. If you also want `fromJson` and `toJson`, the README notes that you additionally need `json_annotation` and `json_serializable`, which is Freezed delegating serialization to a separate generator.

The generator itself is one long-running command:

```
dart run build_runner watch -d
```

The watch flag is what you want during development, since it regenerates on save. The trailing `-d` is passed through to build_runner for verbose diagnostics.

Using it also requires two things at the top of every file: importing the annotation, and a `part` directive pointing at the generated file.

```dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'my_file.freezed.dart';
```

The README notes you may also want to import `package:flutter/foundation.dart`, which brings in classes that make a generated object's output readable in Flutter.

## One analyzer setting to silence a warning

There is a configuration step that catches most new users, and it is short enough to include here. If you plan to use Freezed together with `json_serializable`, recent versions of `json_serializable` and `meta` may require you to disable the `invalid_annotation_target` warning.

The fix goes in `analysis_options.yaml` at the root of your project:

```yaml
analyzer:
  errors:
    invalid_annotation_target: ignore
```

This is a small thing, but it is the kind of small thing that costs an afternoon when you are working from an older tutorial. The conditional wording matters too: the README says this applies when combining Freezed with `json_serializable`, not universally, so if you are not generating JSON you may not need it.

Beyond this, the index lists two levels of configuration, which is a useful sign about how the project expects to be used. One section covers changing behaviour for a specific model and another covers changing it for an entire project, which in practice means you will be able to override defaults globally rather than annotating every class.

## What the index promises about constructors, copyWith and unions

Since the README's body here is documentation-forward, the index is the most useful artefact in it. Under primary constructors it lists: adding getters and methods to your models, asserts, default values, non-constant default values, extending classes, defining a mutable class instead of an immutable one, and allowing mutation of lists, maps and sets.

That list is the honest version of what the tool does. It is not only an immutability enforcer, because there is a documented path to a mutable class and another to permit mutation of collection types. That matters more than it first appears, because a strictly immutable model is sometimes wrong for your problem and a generator that only offers one answer is a generator you end up fighting.

CopyWith has its own section with a deep copy subsection, which implies two behaviours exist: a shallow clone where nested objects are shared, and something closer to a real deep copy. Getting this wrong is the classic source of state bugs in immutable Dart code, so the fact that it is a documented, configurable decision rather than an accident is the point.

Serialization has its own group covering `fromJSON` for classes with multiple constructors and deserializing generic classes. Multiple-constructor deserialization is the hard problem in generated serializers, and listing it separately signals that it was designed for rather than stumbled into.

Union types get the most space. There is a section on shared properties, one on pattern matching to read non-shared properties, one on mixins and interfaces for individual union classes, one on ejecting an individual case, and a legacy group for the older `when` and `Map` utilities. The presence of a legacy group alongside newer pattern matching implies a migration in progress within the feature, so if you are starting fresh read the newer sections first.

## IDE extensions, linting and a Flutter Favorite badge

Under utilities the README lists extensions for VSCode and for IntelliJ and Android Studio, a linting section, and a third-party tool called DartJ. Extensions for both major IDE families is a meaningful signal: this package is used widely enough that editor support is maintained separately from the generator.

The project carries a Flutter Favorite badge, which is a programme designation rather than a popularity metric, and a Discord badge alongside a build badge and a pub.dev version badge. A README translation row at the top links English, Korean, Simplified Chinese, Japanese and Vietnamese versions under `resources/translations`, which is a better signal of adoption than any star count.

There is a migration path worth noting. The README points to a changelog entry and a migration guide for moving from 2.0.0 to 3.0.0, dated 2025-02-25. So a major version has already happened here, and the migration is documented rather than left to the issue tracker.

The repository root is also a monorepo of sorts: `packages/` holds the actual packages, and there is a `benchmarks/` directory, which suggests the author pays attention to generator performance. The `package.json` at the root is unusual for a Dart project and says so itself, in a comment, that it exists only to configure sponsorkit. There is a `sponsor.config.ts` and a `sponsorkit/` directory, so the sponsorship tooling is the only reason JavaScript tooling is present.

## Reading the metadata against the documentation

A few things about the repository state are worth knowing before you commit to the dependency, and one of them is a genuine gap.

The repository has no license recorded. The license field in the repository metadata is empty, and the repository tree at the root lists `packages/` and `resources/` but no LICENSE file at the top level. Meanwhile the README's badge points at the pub.dev page, where a license is stated. Both facts can hold at once, since a monorepo can carry a license inside each package rather than at the root, but if license compliance matters to your organisation you should confirm it on the individual package rather than assume from the GitHub repository page.

The topics list `hacktoberfest`, `code-generator`, `dart`, `flutter` and `union-types`, which describes the ecosystem rather than the mechanics.

On activity: the repository is not archived, the last push was 2026-09-18, and there are no GitHub releases, even though the README documents a 3.0.0 release. That is consistent with a Dart package, where pub.dev is the distribution channel and GitHub tags are secondary, but it does mean you should read the pub.dev version rather than the GitHub release list to know what you have installed.

The 114 open issues against 2,194 stars is the one number that stands out. For a project at this maturity that is a busy queue, and it is worth a skim before you upgrade, since issue threads are where the practical edges of a code generator get reported.

## Conclusion

Freezed earns its place in a Flutter or Dart codebase with many model classes, because the boilerplate it removes is genuinely long and genuinely bug-prone, and because generated equality and copyWith are consistent by construction rather than by review. Read the index before adopting it: the deep copy behaviour, the union type pattern matching and the project-wide configuration options are all documented, and all three change how you design your models. The costs are the usual build_runner tax, an extra analyzer setting to silence a warning, and generated files in your source tree. Install the annotation package and the generator, run build_runner watch, and start with one model that has a nested object before converting a screen's worth of them.

## FAQ

### Is it correct to use freezed?

Freezed is a build_runner code generator for immutable Dart and Flutter models that writes equality, hashCode, copyWith, unions and JSON serialization from one annotated class. It suits codebases with many model classes, and the trade is a generated file in your source tree plus an extra analyzer setting in analysis_options.yaml.

### How do I install Freezed in a Flutter project?

Run flutter pub add with dev:build_runner, freezed_annotation and dev:freezed, then run dart run build_runner watch -d. Each file also needs to import package:freezed_annotation/freezed_annotation.dart and declare a part directive for the generated file.

### What is the difference between Freezed and frozen?

The spelling in the package name is freezed, a deliberate pun, and the repository exists on pub.dev as the Dart package freezed. It is unrelated to the English word frozen, which is what dictionary lookups for the name return.

### Does Freezed generate fromJson and toJson?

It can, and it delegates that work rather than doing it. The install section says to also add json_annotation and json_serializable if you need fromJson and toJson. When you use that combination you may need to set invalid_annotation_target to ignore under analyzer errors in analysis_options.yaml.

## Sources

- [Issues](https://github.com/rrousselGit/freezed/issues)
- [Project website](https://pub.dev/packages/freezed)
- [README](https://github.com/rrousselGit/freezed/blob/master/README.md)
- [rrousselGit/freezed on GitHub](https://github.com/rrousselGit/freezed)

---

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