> For the complete documentation index, see [llms.txt](https://docs.elsaworkflows.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.elsaworkflows.io/application-types/elsa-studio.md).

# Elsa Studio

Elsa Studio is a Blazor application that let's you manage workflows through a UI. The application is essentially a SPA that connects to an Elsa Server as its back-end.

## Setup <a href="#setup" id="setup"></a>

To setup Elsa Studio, we'll go through the following steps:

{% hint style="warning" %}
**Deprecation warning**

The `blazorwasm-empty` template is [dicontinued since .NET 8.0](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new-sdk-templates).\
If you are using .NET 8.0+, you can just use `blazorwasm` instead of `blazorwasm-empty`.
{% endhint %}

1. **Create a New Blazor Webassembly App**

   Execute the following command in the terminal:

   ```bash
   dotnet new blazorwasm -n "ElsaStudioBlazorWasm"
   ```

   \\
2. **Add Elsa Studio Packages**

   Navigate to the root directory of your project and integrate the following Elsa Studio packages:

   ```
   cd ElsaStudioBlazorWasm
   dotnet add package Elsa.Studio
   dotnet add package Elsa.Studio.Core.BlazorWasm
   dotnet add package Elsa.Studio.Authentication.ElsaIdentity.BlazorWasm
   dotnet add package Elsa.Studio.Authentication.ElsaIdentity.UI
   dotnet add package Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm
   dotnet add package Elsa.Studio.Localization.BlazorWasm
   dotnet add package Elsa.Api.Client
   ```
3. **Modify Program.cs**

   Open the `Program.cs` file and replace its existing content with the code provided below:

   **Program.cs**

   ```csharp
   using Elsa.Studio.Authentication.ElsaIdentity.BlazorWasm.Extensions;
   using Elsa.Studio.Authentication.ElsaIdentity.HttpMessageHandlers;
   using Elsa.Studio.Authentication.ElsaIdentity.UI.Extensions;
   using Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm.Extensions;
   using Elsa.Studio.Authentication.OpenIdConnect.HttpMessageHandlers;
   using Elsa.Studio.Contracts;
   using Elsa.Studio.Core.BlazorWasm.Extensions;
   using Elsa.Studio.Dashboard.Extensions;
   using Elsa.Studio.Extensions;
   using Elsa.Studio.Localization.BlazorWasm.Extensions;
   using Elsa.Studio.Localization.Models;
   using Elsa.Studio.Models;
   using Elsa.Studio.Shell;
   using Elsa.Studio.Shell.Extensions;
   using Elsa.Studio.Workflows.Designer.Extensions;
   using Elsa.Studio.Workflows.Extensions;
   using Microsoft.AspNetCore.Components.Web;
   using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

   // Build the host.
   var builder = WebAssemblyHostBuilder.CreateDefault(args);
   var configuration = builder.Configuration;

   // Register root components.
   builder.RootComponents.Add<App>("#app");
   builder.RootComponents.Add<HeadOutlet>("head::after");
   builder.RootComponents.RegisterCustomElsaStudioElements();

   // Choose authentication provider.
   // Supported values: "OpenIdConnect" or "ElsaIdentity".
   var authProvider = configuration["Authentication:Provider"];
   if (string.IsNullOrWhiteSpace(authProvider))
       authProvider = "ElsaIdentity";

   Type authenticationHandler;

   if (authProvider.Equals("ElsaIdentity", StringComparison.OrdinalIgnoreCase))
   {
       // Elsa Identity (username/password against Elsa backend) + login UI at /login.
       builder.Services.AddElsaIdentity();
       builder.Services.AddElsaIdentityUI();
       authenticationHandler = typeof(ElsaIdentityAuthenticatingApiHttpMessageHandler);
   }
   else if (authProvider.Equals("OpenIdConnect", StringComparison.OrdinalIgnoreCase))
   {
       // OpenID Connect.
       builder.Services.AddOpenIdConnectAuth(options =>
       {
           configuration.GetSection("Authentication:OpenIdConnect").Bind(options);
       });
       authenticationHandler = typeof(OidcAuthenticatingApiHttpMessageHandler);
   }
   else
   {
       throw new InvalidOperationException($"Unsupported Authentication:Provider value '{authProvider}'. Supported values are 'OpenIdConnect' and 'ElsaIdentity'.");
   }

   // Register shell services and modules.
   var backendApiConfig = new BackendApiConfig
   {
       ConfigureBackendOptions = options => builder.Configuration.GetSection("Backend").Bind(options),
       ConfigureHttpClientBuilder = options => options.AuthenticationHandler = authenticationHandler
   };

   var localizationConfig = new LocalizationConfig
   {
       ConfigureLocalizationOptions = options => configuration.GetSection("Localization").Bind(options)
   };

   builder.Services.AddCore();
   builder.Services.AddShell();
   builder.Services.AddRemoteBackend(backendApiConfig);
   builder.Services.AddDashboardModule();
   builder.Services.AddWorkflowsModule();
   builder.Services.AddLocalizationModule(localizationConfig);

   // Build the application.
   var app = builder.Build();

   await app.UseElsaLocalization();

   // Run each startup task.
   var startupTaskRunner = app.Services.GetRequiredService<IStartupTaskRunner>();
   await startupTaskRunner.RunStartupTasksAsync();

   // Run the application.
   await app.RunAsync();
   ```
4. **Remove Unnecessary Files**

   For a cleaner project structure, delete the following directories and files:

   * wwwroot/css
   * Pages
   * App.razor
   * MainLayout.razor
   * MainLayout.razor.css
   * \_Imports.razor
5. **Generate appsettings.json**

   Within the `wwwroot` directory, create a new `appsettings.json` file and populate it with the following content:

   wwwroot/appsettings.json

   ```json
   {
       "Backend": {
           "Url": "https://localhost:5001/elsa/api"
       },
       "Authentication": {
           "Provider": "OpenIdConnect",
           "OpenIdConnect": {
               "Authority": "https://login.microsoftonline.com/{tenant-id}/v2.0",
               "ClientId": "{client-id}",
               "AuthenticationScopes": [
                   "openid",
                   "profile",
                   "offline_access"
               ],
               "BackendApiScopes": [
                   "api://{backend-api-client-id}/elsa-server-api"
               ]
           }
       },
       "Localization": {
           "DefaultCulture": "en-US",
           "SupportedCultures": [
               "en-US"
           ]
       }
   }
   ```

   `AuthenticationScopes` are used during Studio sign-in. `BackendApiScopes` are used when Studio requests bearer tokens for Elsa Server API calls.

   Because this example is a Blazor WebAssembly host, register `{studio-url}/authentication/login-callback` as the redirect URI and `{studio-url}/authentication/logout-callback` as the logout callback URI. Studio initiates logout at `{studio-url}/authentication/logout`.
6. **Update index.html**

   To conclude the setup, open the `index.html` file and replace its content with the code showcased below:

   **wwwroot/index.html**

   ```html
   <!DOCTYPE html>
   <html>

   <head>
       <meta charset="utf-8"/>
       <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
       <title>Elsa Studio</title>
       <base href="/"/>
       <link rel="apple-touch-icon" sizes="180x180" href="_content/Elsa.Studio.Shell/apple-touch-icon.png">
       <link rel="icon" type="image/png" sizes="32x32" href="_content/Elsa.Studio.Shell/favicon-32x32.png">
       <link rel="icon" type="image/png" sizes="16x16" href="_content/Elsa.Studio.Shell/favicon-16x16.png">
       <link rel="manifest" href="_content/Elsa.Studio.Shell/site.webmanifest">
       <link rel="preconnect" href="https://fonts.googleapis.com">
       <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
       <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
       <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
       <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
       <link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet">
       <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
       <link href="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet" />
       <link href="_content/Radzen.Blazor/css/material-base.css" rel="stylesheet" >
       <link href="_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
       <link href="ElsaStudioBlazorWasm.styles.css" rel="stylesheet">
   </head>

   <body>
   <div id="app">
       <div class="loading-splash mud-container mud-container-maxwidth-false">
           <h5 class="mud-typography mud-typography-h5 mud-primary-text my-6">Loading...</h5>
       </div>
   </div>

   <div id="blazor-error-ui">
       An unhandled error has occurred.
       <a href="" class="reload">Reload</a>
       <a class="dismiss">🗙</a>
   </div>
   <script src="_content/BlazorMonaco/jsInterop.js"></script>
   <script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
   <script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
   <script src="_content/MudBlazor/MudBlazor.min.js"></script>
   <script src="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
   <script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
   <script src="_framework/blazor.webassembly.js"></script>
   </body>

   </html>
   ```

## Launch the Application <a href="#run-application" id="run-application"></a>

To see your application in action, execute the following command:

```bash
dotnet run --urls https://localhost:6001
```

Your application is now accessible at [https://localhost:6001](https://localhost:6001/).

By default, you can log in using:

```
username: admin
password: password
```

## Source Code <a href="#source-code" id="source-code"></a>

The source code for this chapter can be found [here](https://github.com/elsa-workflows/elsa-guides/tree/main/src/installation/elsa-studio/ElsaStudioBlazorWasm)
