# Using TX Text Control in a Blazor Server App with .NET 8

> This tutorial shows how to use the Document Editor of TX Text Control .NET Server in a Blazor Server App with .NET 8. It uses the JSRuntime and DotNetObjectReference classes to communicate between the editor and the server-side ServerTextControl class in .NET code.

> **Note:** This article is outdated and may no longer be accurate.

- **Author:** Bjoern Meyer
- **Published:** 2024-01-25
- **Modified:** 2026-07-17
- **Description:** This tutorial shows how to use the Document Editor of TX Text Control .NET Server in a Blazor Server App with .NET 8. It uses the JSRuntime and DotNetObjectReference classes to communicate between the editor and the server-side ServerTextControl class in .NET code.
- **9 min read** (1606 words)
- **Tags:**
  - ASP.NET
  - Blazor
  - .NET 8
  - Document Editor
- **Web URL:** https://www.textcontrol.com/blog/2024/01/25/using-tx-text-control-in-an-blazor-server-app-with-net-8/
- **LLMs URL:** https://www.textcontrol.com/blog/2024/01/25/using-tx-text-control-in-an-blazor-server-app-with-net-8/llms.txt
- **LLMs-Full URL:** https://www.textcontrol.com/blog/2024/01/25/using-tx-text-control-in-an-blazor-server-app-with-net-8/llms-full.txt
- **GitHub Repository:** https://github.com/TextControl/TXTextControl.Server.Blazor.DotNet8

---

By creating a Razor component using Interop JavaScript, the TX Text Control Document Editor can be integrated and initialized in Blazor. This sample uses the [JSRuntime](https://learn.microsoft.com/en-us/dotnet/api/microsoft.jsinterop.jsruntime?view=aspnetcore-7.0) and [DotNetObjectReference](https://learn.microsoft.com/en-us/dotnet/api/microsoft.jsinterop.dotnetobjectreference?view=aspnetcore-7.0) classes to communicate between the editor and the server-side ServerTextControl class in .NET code.

Applications built with TX Text Control in Blazor require some modifications compared to older .NET versions due to the many changes in .NET 8. The ASP.NET Core Hosted option has been removed and replaced with a new Blazor project template. Prior to .NET 8, the entire Blazor application would run in either Blazor WASM (Web Assembly) or Blazor Server. NET 8 gives you the ability to control the render mode on a per-component basis. This is a huge benefit because you can now use the TX Text Control, which requires server-side rendering, with WASM applications.

There are 4 render modes available for .NET 8 components:

- **Static server-side**  
    Requests are routed to a Razor Component which returns static HTML.
- **Interactive Server**  
    Components are more interactive when run through Blazor Server. Interactive DOM modification is dynamic.
- **Interactive WASM**  
    The component will be executed on the client side (in the browser) and not on the server side.
- **Interactive Auto**  
    The component uses the Blazor Server for the initial request and then uses the Blazor WASM for subsequent requests.

TX Text Control requires server-side rendering and an update after creation, so this example uses **Interactive Server** mode.

#### Data Flow

The diagram below is an illustration of the data flow in this concept. The Text Control Razor component dynamically adds the TX Text Control Document Editor to the page by initializing it with JavaScript. The JSRuntime is used to create an object reference to the created JavaScript file. This object reference calls the JavaScript API of the TX Text Control.

![TX Text Control in Blazor](https://s1-www.textcontrol.com/assets/dist/blog/2024/01/25/a/assets/blazor.webp "TX Text Control in Blazor")

### Creating the Application

Make sure that you downloaded the latest version of Visual Studio 2022 that comes with the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0).

1. In Visual Studio 2022, create a new project by choosing *Create a new project*.
2. Select *Blazor Web App* as the project template and confirm with *Next*.
    
    ![Creating the .NET 8 project](https://s1-www.textcontrol.com/assets/dist/blog/2024/01/25/a/assets/visualstudio1.webp "Creating the .NET 8 project")
3. Choose a name for your project and confirm with *Next*.
4. In the next dialog, choose *.NET 8 (Long-term support)* as the *Framework*. Select *Server* as the *Interactive render mode* and choose *Per page/component* as the *Interactivity location*. Confirm with *Create*.
    
    ![Creating the .NET 8 project](https://s1-www.textcontrol.com/assets/dist/blog/2024/01/25/a/assets/visualstudio2.webp "Creating the .NET 8 project")

#### Adding the NuGet Package

1. In the *Solution Explorer*, select your created project and choose *Manage NuGet Packages...* from the *Project* main menu.
    
    > **Package Source**
    > 
    > Select either **Text Control Offline Packages** or **nuget.org** as the *Package source*. Packages in the official *Text Control* NuGet profile are frequently updated.
    
    Browse and install the following packages:
    
    
    - *TXTextControl.Web*
    - *TXTextControl.TextControl.ASP.SDK*
    
    ![ASP.NET Core Web Application](https://s1-www.textcontrol.com/assets/dist/blog/2024/01/25/a/assets/visualstudio3.webp "ASP.NET Core Web Application")

#### Configure the Application

1. Open the *Program.cs* file located in the project's root folder.
    
    At the very top of the file, insert the following code:
    
    ```
    using TXTextControl.Web;
    ```
    
    Add the following code after the entry `app.UseStaticFiles();`:
    
    ```
    // enable Web Sockets
    app.UseWebSockets();
    
    // attach the Text Control WebSocketHandler middleware
    app.UseTXWebSocketMiddleware();
    ```

#### Creating the Interop JavaScript

1. Create a new folder *scripts* in your *wwwroot* folder, create a new JavaScript file and name it *textcontrol.js*. Paste the following content into the file you have just created.
    
    ```
    var dotNetObject;
    
    export async function addEditorToElement(dotNetRef, options) {
      dotNetObject = dotNetRef;
    
      TXTextControl.init({
        containerID: "txDocumentEditorContainer",
        webSocketURL: options.websocketurl
      });
    
    }
    
    export function saveDocument() {
    
      // save the document on TXTextControl object
      TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, function (document) {
        // call the .NET method 'ProcessDocument' with the saved document data
        dotNetObject.invokeMethodAsync('ProcessDocument', document.data);
      });
    
    };
    
    export function loadDocument(document) {
      // load the document back into the editor (TXTextControl)
      TXTextControl.loadDocument(TXTextControl.StreamType.InternalUnicodeFormat, document);
    };
    
    export function insertTable() {
      TXTextControl.tables.add(5, 5, 10, function (e) {
        if (e === true) { // if added
          TXTextControl.tables.getItem(function (table) {
            table.cells.forEach(function (cell) {
    
              cell.setText("Cell text");
    
            });
          }, null, 10);
        }
      })
    };
    ```

#### Creating the Razor Component

This step creates a new Razor component named *TextControl.razor* that initializes both the .NET object reference and the JavaScript object reference.

1. Select the project in the *Solution Explorer* and choose *New Item...* from the *Project* main menu. Select *Razor Component*, name it *TextControl.razor* and confirm with *Add*.
2. Paste the following code into the newly created file:
    
    ```
    @using Microsoft.AspNetCore.Components.Web
    @using Microsoft.JSInterop
    @inject IJSRuntime JsRuntime
    @inject NavigationManager Navigator
    
    <script src="@WebSocketURL/api/TXWebSocket/GetResource?name=tx-document-editor.min.js"></script>
    
    <div id="txDocumentEditorContainer" style="width: @Width; height: @Height;"></div>
    
    @code
    {
        protected override Task OnInitializedAsync()
        {
            return base.OnInitializedAsync();
        }
    
        [Parameter]
        public string? WebSocketURL { get; set; }
    
        [Parameter]
        public string? ContainerID { get; set; }
    
        [Parameter]
        public string? Width { get; set; } = "800px";
    
        [Parameter]
        public string? Height { get; set; } = "600px";
    
        private DotNetObjectReference<TextControl> DotNetReference => DotNetObjectReference.Create(this);
        private IJSObjectReference? _txtextcontrol;
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                _txtextcontrol = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./scripts/textcontrol.js");
    
                var options = new Dictionary<string, object?>();
    
                var webSocketURL = Navigator.BaseUri.Replace("https://", "wss://").Replace("http://", "ws://");
    
                if (WebSocketURL != null)
                {
                    options["websocketurl"] = $"{webSocketURL}api/TXWebSocket";
                    options["baseurl"] = $"{WebSocketURL}api/TXWebSocket";
                }
    
                if (ContainerID != null)
                {
                    options["containerid"] = ContainerID;
                }
    
                await _txtextcontrol.InvokeVoidAsync("addEditorToElement", DotNetReference, options);
            }
        }
    
        public async Task SaveDocument()
        {
            await _txtextcontrol.InvokeVoidAsync("saveDocument", DotNetReference);
        }
    
        [JSInvokable("ProcessDocument")]
        public void ProcessDocument(string document)
        {
            byte[] bDocument;
    
            // create a ServerTextControl instance to load the saved document
            using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl())
            {
                tx.Create();
                tx.Load(Convert.FromBase64String(document), TXTextControl.BinaryStreamType.InternalUnicodeFormat);
    
                // add additional text to the document
                tx.Selection.Text = "This document has been modified by .NET\r\n";
    
                // save back
                tx.Save(out bDocument, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
            }
    
            // invoke the JS function 'loadDocument' to load back to the modified document
            _txtextcontrol.InvokeVoidAsync("loadDocument", Convert.ToBase64String(bDocument));
        }
    
        public async Task InsertTable()
        {
            await _txtextcontrol.InvokeVoidAsync("insertTable");
        }
    
    }
    ```

Any function of the referenced *textcontrol.js* JavaScript file can be called using the created *IJSObjectReference* *\_txtextcontrol*. The *DotNetObjectReference<TextControl>* points to this Razor component. This is passed to the JavaScript functions to save and reload the document.

#### Consuming the TextControl Razor Component

In this step, we are going to use the Razor component we have created on a page.

1. Find the *Home.razor* page in the *Pages* folder and replace the content with the following code:
    
    ```
    @page "/"
    @rendermode @(new InteractiveServerRenderMode(false))
    
    @inject NavigationManager Navigator;
    
    <PageTitle>Home</PageTitle>
    
    <TextControl WebSocketURL=@Navigator.BaseUri
                 ContainerID="txDocumentEditorContainer"
                 @ref="_txtextcontrol">
    </TextControl>
    
    <button @onclick="InsertTable">Insert Table</button>
    
    <button @onclick="SaveDocument">
        Save and Reload Document (using ServerTextControl)
    </button>
    
    @code
    {
    
        private TextControl _txtextcontrol = default!;
    
        // insert a table using the client-side API
        private async Task InsertTable()
        {
            await _txtextcontrol.InsertTable();
        }
    
        // save the document and reload it using ServerTextControl
        private async Task SaveDocument()
        {
            await _txtextcontrol.SaveDocument();
        }
    
    }
    ```

Compile and start the application.

![Text Control in Blazor Server](https://s1-www.textcontrol.com/assets/dist/blog/2024/01/25/a/assets/blazor1.webp "Text Control in Blazor Server")

#### First Button: Insert Table

The *InsertTable()* method is called in the .NET code of *Index.razor* when the first button is clicked.

```
private async Task InsertTable()
{
    await _txtextcontrol.InsertTable();
}
```

This calls the *InsertTable()* method of the *TextControl.Razor* component.

```
public async Task InsertTable()
{
    await _txtextcontrol.InvokeVoidAsync("insertTable");
}
```

In this method, the JavaScript object reference *\_txtextcontrol* is used to invoke a JavaScript call to *textcontrol.js*.

```
export function insertTable() {
  TXTextControl.tables.add(5, 5, 10, function (e) {
    if (e === true) { // if added
      TXTextControl.tables.getItem(function (table) {
        table.cells.forEach(function (cell) {

          cell.setText("Cell text");

        });
      }, null, 10);
    }
  })
};
```

#### Second Button: Save and Reload

TX Text Control's JavaScript API is used to store the document. The document is passed to .NET (the "server"). In .NET, the document is processed using an instance of ServerTextControl and sent back to the client side by calling a JavaScript function that loads the content back into the TX Text Control document editor.

The *SaveDocument()* method is called in the .NET code of *Index.razor* when the second button is clicked.

```
private async Task SaveDocument()
{
    await _txtextcontrol.SaveDocument();
}
```

This calls the *SaveDocument()* method of the *TextControl.Razor* component.

```
export function saveDocument() {

  // save the document on TXTextControl object
  TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, function (document) {
    // call the .NET method 'ProcessDocument' with the saved document data
    dotNetObject.invokeMethodAsync('ProcessDocument', document.data);
  });

};
```

This calls the JavaScript function *saveDocument()* in the *textcontrol.js* JavaScript file:

After the document is saved, it is passed to the .NET *ProcessDocument* method implemented in *TextControl.razor* using the created .NET object reference.

```
[JSInvokable("ProcessDocument")]
 public void ProcessDocument(string document)
 {
     byte[] bDocument;

     // create a ServerTextControl instance to load the saved document
     using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl())
     {
         tx.Create();
         tx.Load(Convert.FromBase64String(document), TXTextControl.BinaryStreamType.InternalUnicodeFormat);

         // add additional text to the document
         tx.Selection.Text = "This document has been modified by .NET\r\n";

         // save back
         tx.Save(out bDocument, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
     }

     // invoke the JS function 'loadDocument' to load back to the modified document
     _txtextcontrol.InvokeVoidAsync("loadDocument", Convert.ToBase64String(bDocument));
 }
```

This method is parametrized as *JSInvokable* and can be called from JavaScript.

This method loads the document into a ServerTextControl instance to add text at the top. The document is then saved and passed to the *loadDocument* JavaScript function.

The JavaScript function *loadDocument* is finally loading the document back into the editor.

```
export function loadDocument(document) {
  // load the document back into the editor (TXTextControl)
  TXTextControl.loadDocument(TXTextControl.StreamType.InternalUnicodeFormat, document);
};
```

![Text Control in Blazor Server](https://s1-www.textcontrol.com/assets/dist/blog/2024/01/25/a/assets/blazor2.webp "Text Control in Blazor Server")

For your own testing, you can download and try this sample from our GitHub repository.

---

## About Bjoern Meyer

As CEO, Bjoern is the visionary behind our strategic direction and business development, bridging the gap between our customers and engineering teams. His deep passion for coding and web technologies drives the creation of innovative products. If you're at a tech conference, be sure to stop by our booth - you'll most likely meet Bjoern in person. With an advanced graduate degree (Dipl. Inf.) in Computer Science, specializing in AI, from the University of Bremen, Bjoern brings significant expertise to his role. In his spare time, Bjoern enjoys running, paragliding, mountain biking, and playing the piano.

- [LinkedIn](https://www.linkedin.com/in/bjoernmeyer/)
- [X](https://x.com/txbjoern)
- [GitHub](https://github.com/bjoerntx)

---

## Related Posts

- [Building an ASP.NET Core Backend (Linux and Windows) for the Document Editor and Viewer](https://www.textcontrol.com/blog/2025/03/26/building-an-asp-net-core-backend-for-the-document-editor-and-viewer/llms.txt)
- [TX Text Control for Blazor: Mail Merge Integration Tutorial](https://www.textcontrol.com/blog/2025/03/25/tx-text-control-for-blazor-mail-merge-integration-tutorial/llms.txt)
- [TX Text Control Document Editor and Viewer for Blazor Released](https://www.textcontrol.com/blog/2025/03/25/tx-text-control-document-editor-and-viewer-for-blazor-released/llms.txt)
- [Getting Started: Document Editor for Blazor in ASP.NET Core](https://www.textcontrol.com/blog/2025/03/25/getting-started-document-editor-for-blazor-in-asp-net-core/llms.txt)
- [Announcing Our Work on a Blazor Component for Document Editing and Viewing](https://www.textcontrol.com/blog/2025/01/24/announcing-our-work-on-a-blazor-component-for-document-editing-and-viewing/llms.txt)
- [Handling Electronic Signatures in a Blazor Web App using .NET 8](https://www.textcontrol.com/blog/2024/02/06/handling-electronic-signatures-in-a-blazor-web-app-using-net-8/llms.txt)
- [Using the Document Viewer in a Blazor Server App with .NET 8](https://www.textcontrol.com/blog/2024/01/29/using-the-document-viewer-in-a-blazor-server-app-with-net-8/llms.txt)
- [Using TX Text Control in an ASP.NET Core Blazor Server App](https://www.textcontrol.com/blog/2023/10/05/using-tx-text-control-in-a-blazor-server-app/llms.txt)
- [Beyond WebSockets: A Glimpse into the Future of Document Editing with WebAssembly](https://www.textcontrol.com/blog/2026/06/10/beyond-websockets-glimpse-future-document-editing-webassembly/llms.txt)
- [5 Layout Patterns for Integrating the TX Text Control Document Editor in ASP.NET Core C#](https://www.textcontrol.com/blog/2026/04/09/5-layout-patterns-for-integrating-the-tx-text-control-document-editor-in-aspnet-core-csharp/llms.txt)
- [Introducing Text Control Agent Skills](https://www.textcontrol.com/blog/2026/03/27/introducing-text-control-agent-skills/llms.txt)
- [Deploying the TX Text Control Document Editor from the Private NuGet Feed to Azure App Services (Linux and Windows)](https://www.textcontrol.com/blog/2026/03/25/deploying-the-tx-text-control-document-editor-from-the-private-nuget-feed-to-azure-app-services-linux-and-windows/llms.txt)
- [Build a Custom Backstage View in ASP.NET Core with TX Text Control](https://www.textcontrol.com/blog/2026/02/17/build-a-custom-backstage-view-in-aspnet-core-with-tx-text-control/llms.txt)
- [ASP.NET Core Document Editor with Backend via the Text Control Private NuGet Feed](https://www.textcontrol.com/blog/2026/02/09/aspnet-core-document-editor-private-nuget-feed/llms.txt)
- [Why Document Processing Libraries Require a Document Editor](https://www.textcontrol.com/blog/2025/12/04/why-document-processing-libraries-require-a-document-editor/llms.txt)
- [Getting Started Video Tutorial: Document Editor in ASP.NET Core C# on Linux](https://www.textcontrol.com/blog/2025/07/29/getting-started-video-tutorial-document-editor-aspnet-core-csharp-linux/llms.txt)
- [Document Protection in ASP.NET with TX Text Control: Healthcare Use Cases](https://www.textcontrol.com/blog/2025/06/25/document-protection-in-asp-dotnet-with-tx-text-control-healthcare-use-cases/llms.txt)
- [E-Sign Comes to Blazor: Document Viewer 33.0.1 Released](https://www.textcontrol.com/blog/2025/04/24/e-sign-comes-to-blazor-document-viewer-33-0-1-released/llms.txt)
- [Deploying the TX Text Control Document Editor in an ASP.NET Core Web App to Azure App Services](https://www.textcontrol.com/blog/2025/03/26/deploying-the-tx-text-control-document-editor-in-an-asp-net-core-web-app-to-azure-app-services/llms.txt)
- [Getting Started: Document Viewer for Blazor in ASP.NET Core](https://www.textcontrol.com/blog/2025/03/25/getting-started-document-viewer-for-blazor-in-asp-net-core/llms.txt)
- [Introducing TXTextControl.Web.Server.Core: A Cross-Platform Backend for TX Text Control Document Editor](https://www.textcontrol.com/blog/2025/03/13/introducing-txtextcontrol-web-server-core-a-cross-platform-backend-for-tx-text-control-document-editor/llms.txt)
- [Getting Started: Document Editor with ASP.NET Core and Docker Support with Linux Containers](https://www.textcontrol.com/blog/2025/03/12/getting-started-document-editor-with-asp-net-core-and-docker-support-with-linux-containers/llms.txt)
- [Preparing Documents for E-Signing for Multiple Signers in .NET C#](https://www.textcontrol.com/blog/2024/11/13/preparing-documents-for-e-signing-for-multiple-signers-in-net-c-sharp/llms.txt)
- [ASP.NET Core: Use the Document Editor and Viewer in the Same Razor View](https://www.textcontrol.com/blog/2024/11/08/asp-net-core-use-the-document-editor-and-viewer-in-the-same-razor-view/llms.txt)
- [Connecting the TXWebSocketMiddleware to a Separate, External TCP Synchronization Service](https://www.textcontrol.com/blog/2024/10/01/connecting-the-txwebsocketmiddleware-to-a-separate-external-tcp-synchronization-service/llms.txt)
