diff --git a/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs
new file mode 100644
index 0000000000..a1d1eb96a3
--- /dev/null
+++ b/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs
@@ -0,0 +1,147 @@
+// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components;
+
+namespace ReactiveUI.Blazor
+{
+ ///
+ /// A base component for handling property changes and updating the blazer view appropriately.
+ ///
+ /// The type of view model. Must support INotifyPropertyChanged.
+ public class ReactiveInjectableComponentBase : ComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate, IDisposable
+ where T : class, INotifyPropertyChanged
+ {
+ private readonly Subject _initSubject = new Subject();
+ [SuppressMessage("Design", "CA2213: Dispose object", Justification = "Used for deactivation.")]
+ private readonly Subject _deactivateSubject = new Subject();
+ private readonly CompositeDisposable _compositeDisposable = new CompositeDisposable();
+
+ private T _viewModel;
+
+ private bool _disposedValue; // To detect redundant calls
+
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ [Inject]
+ public T ViewModel
+ {
+ get => _viewModel;
+ set
+ {
+ if (EqualityComparer.Default.Equals(_viewModel, value))
+ {
+ return;
+ }
+
+ _viewModel = value;
+ OnPropertyChanged();
+ }
+ }
+
+ ///
+ object IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = (T)value;
+ }
+
+ ///
+ public IObservable Activated => _initSubject.AsObservable();
+
+ ///
+ public IObservable Deactivated => _deactivateSubject.AsObservable();
+
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) below.
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ protected override void OnInitialized()
+ {
+ _initSubject.OnNext(Unit.Default);
+ base.OnInitialized();
+ }
+
+ ///
+ protected override void OnAfterRender(bool firstRender)
+ {
+ if (firstRender)
+ {
+ // The following subscriptions are here because if they are done in OnInitialized, they conflict with certain JavaScript frameworks.
+ var viewModelChanged =
+ this.WhenAnyValue(x => x.ViewModel)
+ .Where(x => x != null)
+ .Publish()
+ .RefCount(2);
+
+ viewModelChanged
+ .Subscribe(_ => InvokeAsync(StateHasChanged))
+ .DisposeWith(_compositeDisposable);
+
+ viewModelChanged
+ .Select(x =>
+ Observable
+ .FromEvent(
+ eventHandler =>
+ {
+ void Handler(object sender, PropertyChangedEventArgs e) => eventHandler(Unit.Default);
+ return Handler;
+ },
+ eh => x.PropertyChanged += eh,
+ eh => x.PropertyChanged -= eh))
+ .Switch()
+ .Subscribe(_ => InvokeAsync(StateHasChanged))
+ .DisposeWith(_compositeDisposable);
+ }
+
+ base.OnAfterRender(firstRender);
+ }
+
+ ///
+ /// Invokes the property changed event.
+ ///
+ /// The name of the property.
+ protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ ///
+ /// Cleans up the managed resources of the object.
+ ///
+ /// If it is getting called by the Dispose() method rather than a finalizer.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _initSubject?.Dispose();
+ _compositeDisposable?.Dispose();
+ _deactivateSubject.OnNext(Unit.Default);
+ }
+
+ _disposedValue = true;
+ }
+ }
+ }
+}