ReactBlazorAdapter 1.0.2

dotnet add package ReactBlazorAdapter --version 1.0.2
                    
NuGet\Install-Package ReactBlazorAdapter -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ReactBlazorAdapter" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ReactBlazorAdapter" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="ReactBlazorAdapter" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ReactBlazorAdapter --version 1.0.2
                    
#r "nuget: ReactBlazorAdapter, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ReactBlazorAdapter@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ReactBlazorAdapter&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=ReactBlazorAdapter&version=1.0.2
                    
Install as a Cake Tool

ReactBlazorAdapter

What is it?

This project aims to be a lightweight adapter that allows embedding React components in Blazor while integrating the React lifecycle and providing support for callbacks to Blazor components. When your Blazor component is disposed, the react component tree will be unmounted (ie: event handlers managed by react will be unsubscribed).

Setup

Blazor WebAssembly Standalone App

.NET MAUI Blazor Hybrid App

Adding a nuget Reference

Like any other nuget package, you can use the dotnet CLI:

# for Blazor WebAssembly Standalone App
dotnet add package ReactBlazorAdapter

# for .NET MAUI Blazor Hybrid App
dotnet add package ReactBlazorAdapter.RCL

Or use your favorite IDE to add a nuget package reference to the consuming Blazor WASM project.

Including Adapter JavaScript

For Blazor projects hosted by Visual Studio/Rider, you can add a reference to your index.html file such as:


<script src="/_content/ReactBlazorAdapter/ReactBlazorAdapter.js"></script>

<script src="static/js/main.d5dccc3d.js"></script>

<script src="_framework/blazor.webassembly.js"></script>

Including Adapter JavaScript MAUI

Add the ReactBlazorAdapter.MAUI.js reference to your index.html file:


<script src="_content/ReactBlazorAdapter.RCL/ReactBlazorAdapter.MAUI.js"></script>

<script src="./static/js/my-react-bundle.js"></script>
<script src="_framework/blazor.webview.js" autostart="false"></script>

Initializing ReactBlazorAdapter

ReactBlazorAdapter is created as a window-level variable (ie: accessible from either window.ReactBlazorAdapter or globalThis.ReactBlazorAdapter), due to the need to interop between JavaScript and Blazor.

Within your React code, add the following:

import React from 'react'
import ReactDOM from 'react-dom/client' //React 18 is from 'react-dom'
import FooComponent from './path/to/FooComponent'
// Pass React and ReactDOM to initialize()
ReactBlazorAdapter.initialize(React, ReactDOMClient)
// Register your top-level react components that Blazor will consume
// "fooname" is an arbitrary alias that is shared with Blazor passed to the ComponentName attribute in Blazor
ReactBlazorAdapter.registerComponent('fooname', FooComponent)

Using React Components

Within your Blazor markup, add the using directive and leverage the ReactComponent Blazor component.

@using ReactBlazorAdapter.Components

<ReactComponent
        ComponentName="foo"
        ElementId="foocontainer"
/>

If using MAUI, change the using to @using ReactBlazorAdapter.RCL.Components

Getting a ref to class components

While ReactBlazorAdapter works with top-level hooks components, React does not allow setting a ref for hooks-based components. React.forwardRef is not supported by ReactBlazorAdapter, but you can obtain a ref to a React class-based component by passing a @ref attribute.

<ReactComponent
        ComponentName="foo"
        ElementId="foocontainer"
        @ref="FooComponent"
        message="@MessageFromBlazor"
/>

@code {
    public ReactComponent FooComponent { get; set; }
    public string MessageFromBlazor { get; set; } = "Hello world!";
}

Invoking instance methods on class-based React components

If your class-based component has instance methods, you can invoke them from Blazor within the @code {...} block:

    // ie given a React component
    class FooComponent extends React.Component {
        showAlert(message) {
          alert(message);
          return 42;
        }
        //...
    }
    // you can invoke 'showAlert', which returns an int and accepts a string:
    private async Task CallFromBlazor()
    {
        LastReactValue = await FooComponent.InvokeMethod<int>("showAlert", new object[]
        {
            "Hello from Blazor!"
        });
    }

Receiving callbacks from React components in Blazor

Receiving callbacks from React requires passing a reference to the current Blazor component, so that it can be accessed from JavaScript, and making a [JSInvokable] method in your Blazor component. Also, the exact prop name from React should be used in Blazor markup and match with the [JSInvokable] attribute.

<ReactComponent
        ComponentName="foo"
        ElementId="foocontainer"
        CallbackTo="this"
        onCountUpdated="@OnUpdatedCallback"
/>

@code {
    public int LastReactValue { get; set; }

    [JSInvokable("onCountUpdated")]
    public void OnUpdatedCallback(int value)
    {
        Console.WriteLine("Blazor updated by react! " + value);
        LastReactValue = value;
        StateHasChanged();
    }
}

Updating props from Blazor

Props passed to React are updated the same way other state in Blazor markup is updated, ie using StateHasChanged()

<ReactComponent
        ComponentName="foo"
        ElementId="foocontainer"
        CallbackTo="this"
        message="@Message"
/>
@code {
  public string Message { get; set; } = "initial";
  // ...
  public void UpdateMessage() {
    Message = $"Updated at {DateTime.Now.ToShortTimeString()}";
    StateHasChanged();
  }
}

Q&A

How can I change the wrapping element?

The Blazor ReactComponent by default will create a div to mount the top-level React component to. You can change the element using the ElementName Blazor attribute.

<ReactComponent
        ComponentName="foo"
        ElementId="foocontainer"
        ElementName="customelement"
/>

Why didn't my component show up?

  • Make sure you've called ReactBlazorAdapter.initialize() with React and ReactDOM
  • Make sure you've registered your component using ReactBlazorAdapter.registerComponent()
  • Check the developer log carefully for all console errors

What versions of React and .NET are supported?

Currently only .NET 8.0 and React 18 are supported, but I'd like to add support for older versions of React and .NET 6.0 in the future.

I have a create-react-app bundle, how can I use it?

I hope to add some demo examples to this repository soon. In the meantime... in your App.js file, add:

/*global globalThis*/
// the above line informs eslint that this standard global variable exists
// add React and ReactDOM explicit imports:
import React from 'react';
import ReactDOM from 'react-dom';
import SampleComponent from 'path/to/component';

//...App component code...

// initialize and register components
globalThis["ReactBlazorAdapter"].initialize(React, ReactDOM);
globalThis["ReactBlazorAdapter"].registerComponent('sample-app', App);
globalThis["ReactBlazorAdapter"].registerComponent('sample-component', SampleComponent);

export default App

Add the compiled output to wwwroot/static/js (ie "main" and "chunk" files). Reference them from index.html after the ReactBlazorAdapter.js file and before blazor.webview.js (ie for MAUI).

As create-react-app expects a div with id "root", if you do not wish to eject and customize your create-react-app, you may want to add a <div id="root" style="display: none;" /> to your index.html file to avoid a console error.

Passing methods as attributes to ReactComponent causes Visual Studio to suggest invoking the method (CS8974)

This seems to be an open issue with the C# toolchain that has been fixed before: https://github.com/dotnet/roslyn/issues/60423 https://github.com/dotnet/roslyn/issues/68307

One way to prevent these warnings is to cast the delegate to object explicitly, for example:

onCountUpdated="@((object)OnUpdatedCallback)"
// vs
onCountUpdated="@OnUpdatedCallback"

This project is licensed under the Apache 2.0 license (see LICENSE.md). © 2024 Katie Michelle Snead

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 133 6/4/2024
1.0.1 129 6/4/2024
1.0.0 119 6/2/2024

* Update README content - no code changes