This documentation covers using ReactiveUI Source Generators to simplify and enhance the use of ReactiveUI objects.
- Minimum Requirements:
- C# Version: 12.0
- Visual Studio Version: 17.14.0 (Roslyn 4.14) — or Visual Studio 2026 (Roslyn 5.0)
- ReactiveUI Version: 23.2.28+
- Installation
- Overview
- Supported Attributes & Features
- Detailed Usage
- Moved to ReactiveUI.Binding
- Compatibility Notes
- Credits
dotnet add package ReactiveUI.SourceGenerators
Ensure the package is loaded with PrivateAssets="all" to avoid issues with generated code in consuming projects.
ReactiveUI V24.x.x consumers can reference either ReactiveUI for the primitives-based API without
System.Reactive, or ReactiveUI.Reactive for the System.Reactive-based API. The generators detect
the referenced API surface automatically. ReactiveUI releases from 23.2.28 remain supported.
ReactiveUI Source Generators automatically generate ReactiveUI objects to streamline your code. These Source Generators are designed to work with ReactiveUI V23.2.28+ and support the following features:
[Reactive]With field and access modifiers, partial property support (C# 13 Visual Studio Version 17.12.0), partial properties with initializer support (C# 14+/preview only)[Reactive(SetModifier = AccessModifier.Protected)]With field and access modifiers, (Not Required for partial properties, configure set accessor with the property declaration).[Reactive(Inheritance = InheritanceModifier.Virtual)]With field and access modifiers. This will generate a virtual property.[Reactive(UseRequired = true)]With field and access modifiers. This will generate a required property, (Not Required for partial properties, use required keyword for property declaration).[Reactive(nameof(RaiseProperty1), nameof(RaiseProperty2))]With field and property changed notification for additional properties.[ReactiveCommand][ReactiveCommand(RunInBackground = true)]runs a synchronous command on ReactiveUI's background scheduler[ReactiveCommand(CanExecute = nameof(IObservableBoolName))]with CanExecute[ReactiveCommand(OutputScheduler = "RxSchedulers.MainThreadScheduler")]using a ReactiveUI Scheduler[ReactiveCommand(OutputScheduler = nameof(_isheduler))]using a Scheduler defined in the class[ReactiveCommand][property: AttributeToAddToCommand]with Attribute passthrough[ReactiveCommand(AccessModifier = PropertyAccessModifier.Internal)]sets the access modifier of the generated command property[RoutedControlHost("YourNameSpace.CustomControl")][ViewModelControlHost("YourNameSpace.CustomControl")][BindableDerivedList]Generates a derived list from a ReadOnlyObservableCollection backing field[ReactiveCollection]Generates property changed notifications on add, remove, new actions on a ObservableCollection backing field[IReactiveObject]Generates IReactiveObject implementation for classes not able to inherit from ReactiveObject
[ObservableAsProperty], view registration and [IViewFor] have been removed in favour of ReactiveUI.Binding; see Moved to ReactiveUI.Binding.
- For .NET Framework 4.8 and older, add Polyfill by Simon Cropp or PolySharp by Sergio Pedri to your project and set the
LangVersionto 12.0 or later in your project file.
The generators ship three Roslyn analyzer bands and the matching one is selected automatically by your compiler (it picks the highest band that is less than or equal to its own Roslyn version):
Band (analyzers/dotnet/...) |
Roslyn | Minimum tooling | Covers |
|---|---|---|---|
roslyn4.8 |
4.8 | Visual Studio 2022 17.8, .NET 8 SDK (8.0.1xx) | The whole .NET 8 SDK line (Roslyn 4.8–4.11) plus VS 17.12/17.13 (Roslyn 4.12/4.13) |
roslyn4.14 |
4.14 | Visual Studio 2022 17.14, .NET SDK 9.0.3xx | VS 17.14, current .NET 9 SDK |
roslyn5.0 |
5.0 | Visual Studio 2026, .NET 10 SDK | VS 2026, .NET 10 SDK |
The roslyn4.8 baseline keeps the entire .NET 8 SDK line working — .NET 8 is supported through November 2026 and its SDK feature bands carry Roslyn 4.8 (8.0.1xx) through 4.11 (8.0.4xx); all of them select the roslyn4.8 band. The 4.8 band compiles against the Roslyn 4.8 API only (the 4.12+ surface is compiled out), so it loads on those compilers without the CS9057 "analyzer references a newer compiler" error. See the official Roslyn package version mappings.
| Visual Studio 2022 LTSC | Roslyn | .NET SDK band |
|---|---|---|
| 17.8 | 4.8 | 8.0.1xx |
| 17.9 | 4.9 | 8.0.2xx |
| 17.10 | 4.10 | 8.0.3xx |
| 17.11 | 4.11 | 8.0.4xx |
| 17.14 | 4.14 | 9.0.3xx |
For more information on analyzer codes, see the analyzer codes documentation.
Marks properties as reactive, generating getter and setter code.
Generates commands, with options to add attributes or enable CanExecute functionality.
Platform-specific attributes for control hosting in WinForms applications.
Generates a derived list from a ReadOnlyObservableCollection backing field.
Generates property changed notifications on add, remove, and new actions on an ObservableCollection backing field.
Generates IReactiveObject implementation for classes not able to inherit from ReactiveObject.
Previously, properties were declared like this:
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}Before these Source Generators were available we used ReactiveUI.Fody.
With ReactiveUI.Fody the [Reactive] Attribute was placed on a Public Property with Auto get / set properties, the generated code from the Source Generator and the Injected code using Fody are very similar with the exception of the Attributes.
[Reactive]
public string Name { get; set; }To migrate from ReactiveUI.Fody to ReactiveUI.SourceGenerators, follow these steps:
- Remove ReactiveUI.Fody: Uninstall the ReactiveUI.Fody NuGet package from your project. Remove the Fody support files from the project directory
- FodyWeaver.xml
- FodyWeavers.xsd
- Install ReactiveUI.SourceGenerators: Add the ReactiveUI.SourceGenerators NuGet package
- Update Property Declarations: Place
[Reactive]attributes on your properties as shown in the examples below and ensure that your classes and properties are declared aspartial. You can also use field backing for[Reactive]properties as shown in the examples. This is a change from ReactiveUI.Fody which only supported property backing. Remove using directives forReactiveUI.Fody.Helpersand add using directives forReactiveUI.SourceGenerators. Fody's[ObservableAsProperty]has no equivalent in this package; see A read-only property backed by an observable. - Rebuild Your Project: Ensure that your project builds successfully and that the generated code behaves as expected.
using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass : ReactiveObject
{
[Reactive]
private string _myProperty;
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass : ReactiveObject
{
[Reactive(SetModifier = AccessModifier.Protected)]
private string _myProperty;
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass : ReactiveObject
{
[Reactive]
[property: JsonIgnore]
private string _myProperty;
}Partial properties are supported in C# 13 and Visual Studio 17.12.0 and later.
Both the getter and setter must be empty, and the [Reactive] attribute must be placed on the property.
Override and Virtual properties are supported.
Set Access Modifier is also supported on partial properties.
using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass : ReactiveObject
{
[Reactive]
public partial string MyProperty { get; set; }
}Partial properties with initial value are supported in C# preview and Visual Studio 17.12.0 and later.
Both the getter and setter must be empty, and the [Reactive] attribute must be placed on the property.
Override and Virtual properties are supported.
Set Access Modifier is also supported on partial properties.
using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass : ReactiveObject
{
[Reactive]
public partial string MyProperty { get; set; } = "Default Value"
}Roslyn source generators don’t have a defined run order and each generator sees the same initial compilation.
Code/attributes that one generator emits aren’t visible to other generators in the same compilation round,
so adding [JsonPropertyName]/[JsonInclude] from ReactiveUI.SourceGenerators won’t cause the System.Text.Json
source generator to pick them up in that project. That’s by design of the generator pipeline (no inter-generator dependencies / ordering).
So System.Text.Json needs special care. In the case that you want to Json-serialize [Reactive]
properties, and you want to use the System.Text.Json source generator they must run in different
assemblies. The same applies to other source generators depending on the output of ReactiveUI.SourceGenerators.
Define types with [Reactive] properties in assembly A, and then define the
System.Text.Json.JsonSerializerContext source generation context in assembly B, and let
B reference A.
ReactiveUI's binding engine, ReactiveUI.Binding, now covers three features this package used to generate. They have been removed from ReactiveUI.SourceGenerators.
| Removed | Use instead |
|---|---|
[ObservableAsProperty] on a field, method, observable property or partial property, and the generated InitializeOAPH() |
ReactiveUI.Binding's [ObservableAsProperty] on a partial property, assigned with ToProperty |
RegisterViewsForViewModelsSourceGenerated(), the RegistrationType and ViewModelRegistrationType options of [IViewFor], and SplatRegistrationType |
ReactiveUI.Binding's view locator, which registers views at compile time |
[IViewFor<T>] and [IViewFor("...")], which generated the ViewModel property and the IViewFor<T> implementation |
Implement IViewFor<T> on the view yourself, or derive from one of ReactiveUI's view base classes; ReactiveUI.Binding's view locator registers it |
| RXUISG0014, RXUISG0017 and the RXUISPR0002 suppression | Nothing: they only applied to [ObservableAsProperty] |
The Windows Forms [RoutedControlHost] and [ViewModelControlHost] remain. They resolve views through ReactiveUI's
view locator, or through ReactiveUI.Binding's when the project's ReactiveUI is built on it.
The ReactiveUI.Binding replacements need a ReactiveUI release built on ReactiveUI.Binding. ReactiveUI 24.3 and earlier
are not: there, ReactiveUI's own ObservableAsPropertyHelper<T> and IViewFor<T> are the ones in use, so
ReactiveUI.Binding's ToProperty does not produce the helper its generated property expects, and its view locator does
not see ReactiveUI's IViewFor<T>. On those releases, use the hand-written forms below.
With ReactiveUI.Binding, mark a partial property [ObservableAsProperty] (C# 13 or later). The generator writes the
property's body and a helper field named after it, which you assign with ToProperty:
using ReactiveUI;
using ReactiveUI.Binding;
public partial class MyReactiveClass : ReactiveObject
{
public MyReactiveClass(IObservable<string> myPropertySource) =>
_myPropertyHelper = myPropertySource.ToProperty(this, x => x.MyProperty, initialValue: "Default Value");
[ObservableAsProperty]
public partial string MyProperty { get; }
}ReactiveUI.Binding 7.11 and later report [ObservableAsProperty] on a field, method or observable property
(RXUIBIND018), and its code fix rewrites each as a partial property.
Without ReactiveUI.Binding, or on ReactiveUI 24.3 and earlier, write the helper with ReactiveUI's ToProperty:
using ReactiveUI;
public class MyReactiveClass : ReactiveObject
{
private readonly ObservableAsPropertyHelper<string> _myPropertyHelper;
public MyReactiveClass(IObservable<string> myPropertySource) =>
_myPropertyHelper = myPropertySource.ToProperty(this, x => x.MyProperty, initialValue: "Default Value");
public string MyProperty => _myPropertyHelper.Value;
}[IViewFor] generated a view's ViewModel property and its IViewFor<T> implementation. Write them on the view, or
derive the view from one of ReactiveUI's view base classes such as ReactiveUserControl<T>:
using ReactiveUI;
public partial class LoginView : UserControl, IViewFor<LoginViewModel>
{
public LoginViewModel? ViewModel { get; set; }
object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (LoginViewModel?)value; }
}ReactiveUI.Binding's view locator registers every class whose declaration implements IViewFor<T>. Without
ReactiveUI.Binding, or on ReactiveUI 24.3 and earlier, register each view with Splat:
AppLocator.CurrentMutable.Register<IViewFor<LoginViewModel>>(static () => new LoginView());using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private void Execute() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private void Execute(string parameter) { }
}Use RunInBackground for synchronous command methods that should be created with ReactiveCommand.CreateRunInBackground. Task- and observable-returning methods continue to use their asynchronous ReactiveCommand factories.
using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand(RunInBackground = true)]
private void ExecuteExpensiveWork() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private string Execute(string parameter) => parameter;
}Note: the Async suffix is removed from the generated command
using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private async Task<string> ExecuteAsync(string parameter) => await Task.FromResult(parameter);
// Generates the following code ExecuteCommand, Note the Async suffix is removed
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private IObservable<string> Execute(string parameter) => Observable.Return(parameter);
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private async Task Execute(CancellationToken token) => await Task.Delay(1000, token);
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand]
private async Task<string> Execute(string parameter, CancellationToken token)
{
await Task.Delay(1000, token);
return parameter;
}
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
private IObservable<bool> _canExecute;
[Reactive]
private string _myProperty1;
[Reactive]
private string _myProperty2;
public MyReactiveClass()
{
_canExecute = this.WhenAnyValue(x => x.MyProperty1, x => x.MyProperty2, (x, y) => !string.IsNullOrEmpty(x) && !string.IsNullOrEmpty(y));
}
[ReactiveCommand(CanExecute = nameof(_canExecute))]
private void Search() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
private IObservable<bool> _canExecute;
[Reactive]
private string _myProperty1;
[Reactive]
private string _myProperty2;
public MyReactiveClass()
{
_canExecute = this.WhenAnyValue(x => x.MyProperty1, x => x.MyProperty2, (x, y) => !string.IsNullOrEmpty(x) && !string.IsNullOrEmpty(y));
}
[ReactiveCommand(CanExecute = nameof(_canExecute))]
[property: JsonIgnore]
private void Search() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand(OutputScheduler = "RxSchedulers.MainThreadScheduler")]
private void Execute() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
private IScheduler _customScheduler = new TestScheduler();
[ReactiveCommand(OutputScheduler = nameof(_customScheduler))]
private void Execute() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[ReactiveCommand(AccessModifier = PropertyAccessModifier.Internal)]
private void Execute() { }
}using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass
{
[BindableDerivedList]
private readonly ReadOnlyObservableCollection<string> _myList;
}using ReactiveUI.SourceGenerators.WinForms;
[RoutedControlHost("YourNameSpace.CustomControl")]
public partial class MyCustomRoutedControlHost;using ReactiveUI.SourceGenerators.WinForms;
[ViewModelControlHost("YourNameSpace.CustomControl")]
public partial class MyCustomViewModelControlHost;using System.Collections.ObjectModel;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
public partial class MyReactiveClass : ReactiveObject
{
[ReactiveCollection]
private ObservableCollection<string> _myCollection;
public MyReactiveClass()
{
MyCollection = new ObservableCollection<string>();
_myCollection.Add("Item 1");
}
}using ReactiveUI;
using ReactiveUI.SourceGenerators;
[IReactiveObject]
public partial class MyReactiveClass
{
[Reactive]
private string _myProperty;
}Portions of this code base are based on and derived from
- PolySharp library. Thanks go to @Sergio0694
- Microsoft MVVM Community Toolkit
JetBrains gives ReactiveUI's maintainers licences for its tools through its open source support programme. Anthropic supports them with Claude through Claude for Open Source. OpenAI supports them with Codex through Codex for Open Source.
See our sponsors for more information. JetBrains, Claude, Anthropic, OpenAI and Codex names and logos are trademarks of their respective owners.