Skip to content

Using Blazor Components

This recipe explains how to add Blazor WebAssembly components to your Astro pages, enabling you to use .NET libraries for physics visualizations and demonstrations.

Overview

Blazor components run .NET code compiled to WebAssembly in the browser. They can coexist with React components and other page content in the same DOM, without requiring iframes.

Prerequisites

  • .NET 8.0 SDK or later
  • Node.js 18.x or later
  • Basic understanding of Blazor and Razor syntax

Project Structure

The Blazor project lives at PAA.Blazor/ in the repository root:

PAA.Blazor/
├── Components/ # Reusable Blazor components
│ ├── PhysicsDemo.razor
│ └── PhysicsDemo.razor.css
├── Pages/ # Page components (Home.razor handles routing)
├── Layout/ # Layout components (simplified, no nav)
├── wwwroot/ # Static assets
└── PAA.Blazor.csproj # Project file

Creating a Blazor Component

1. Create the Component File

Create a new .razor file in PAA.Blazor/Components/:

@* MyComponent.razor *@
<div class="my-component">
<h3>My Physics Demo</h3>
<p>Simulation output: @value</p>
<button @onclick="RunSimulation">Run</button>
</div>
@code {
private double value = 0;
private void RunSimulation()
{
// Your .NET code here
value = Math.PI * Math.E;
}
}

2. Create Component Styles

Create a corresponding .razor.css file for scoped styles:

MyComponent.razor.css
.my-component {
padding: 1rem;
border: 2px solid #007acc;
border-radius: 8px;
}
.my-component h3 {
color: #007acc;
}

Blazor automatically scopes these styles to your component.

3. Register the Component

Automatic Registration: Components in the PAA.Blazor/Components/ directory are automatically discovered and registered during the build process. The build system scans for all .razor files and generates component-config.json automatically.

Dynamic Rendering: The Home.razor page uses Blazor’s DynamicComponent to automatically render any registered component based on the configuration file. No manual code changes needed!

@if (componentType != null)
{
<DynamicComponent Type="@componentType" />
}

The component type is resolved at runtime from the component-config.json mappings. Simply add your component to the Components/ directory and it will be automatically:

  1. Discovered during build
  2. Added to component-config.json
  3. Available for dynamic rendering

Note: The entire registration and rendering process is fully automated. When you add a new component to the Components/ directory, it will be automatically discovered at build time, added to component-config.json, and rendered dynamically without any code changes.

Adding to an Astro Page

1. Single Component Mode (Simple)

In your .mdx or .astro file, add a div with the component name:

<div id="blazor-root" data-component="MyComponent"></div>
<script src="/blazor/_framework/blazor.webassembly.js" autostart="false"></script>
<script src="/blazor-init.js"></script>

The data-component attribute tells Blazor which component to render.

2. Multiple Components Mode (Advanced)

To display multiple Blazor components at different locations on the same page:

<!-- Define target containers for each component -->
<div id="physics-demo-1"></div>
<h2>Wave Simulation</h2>
<div id="wave-sim-1"></div>
<h2>Another Physics Demo</h2>
<div id="physics-demo-2"></div>
<!-- Single Blazor app renders all components -->
<div id="blazor-root" data-components='[
{"name":"PhysicsDemo", "targetId":"physics-demo-1"},
{"name":"WaveSimulation", "targetId":"wave-sim-1"},
{"name":"PhysicsDemo", "targetId":"physics-demo-2"}
]'></div>
<script src="/blazor/_framework/blazor.webassembly.js" autostart="false"></script>
<script src="/blazor-init.js"></script>

Benefits:

  • Share a single Blazor runtime (~2MB) across all components
  • Render multiple instances of the same component
  • Place components anywhere on the page
  • Each component can be positioned independently

How it works:

  1. The blazor-init.js script reads the data-components JSON array
  2. Blazor renders all components inside #blazor-root
  3. JavaScript automatically moves each component to its target element
  4. All components share the same .NET runtime instance

3. The Component Loads Automatically

When the page loads:

  1. The blazor-init.js script reads the data-component attribute (or data-components for multiple)
  2. It passes the component information to Blazor via query parameters
  3. Blazor’s Home.razor reads the parameters and renders the requested components
  4. Components are automatically placed into their target elements
  5. All resources load from /blazor/_framework/

Building and Publishing

Development

During development, run both Astro and rebuild Blazor when needed:

Terminal window
# Build Blazor only
npm run build:blazor
# Start Astro dev server
npm run dev

Production Build

The production build automatically compiles Blazor before Astro:

Terminal window
npm run build

This:

  1. Compiles the Blazor project: dotnet publish ./PAA.Blazor -c Release
  2. Copies output to public/blazor/
  3. Builds the Astro site (which copies to dist/blazor/)

CI/CD

The GitHub Actions workflow (.github/workflows/deploy.yml) automatically:

  1. Installs .NET 8 SDK
  2. Builds the Blazor project
  3. Builds the Astro site
  4. Deploys everything to GitHub Pages

Using .NET Libraries

One of the main benefits of Blazor is access to .NET libraries:

@using System.Numerics
@code {
private void CalculateComplexMath()
{
// Use .NET types and libraries
var complex = new Complex(3.0, 4.0);
var magnitude = Complex.Abs(complex);
// Use LINQ
var primes = Enumerable.Range(2, 100)
.Where(n => IsPrime(n))
.ToList();
}
private bool IsPrime(int n)
{
return Enumerable.Range(2, (int)Math.Sqrt(n) - 1)
.All(i => n % i != 0);
}
}

Best Practices

Keep Components Focused

Each component should represent a single visualization or demo. Keep the logic focused and reusable.

Use Scoped CSS

Always create a .razor.css file alongside your component for styles. This prevents conflicts with other page styles.

Handle Disposal

If your component uses timers or subscriptions, implement IDisposable:

@implements IDisposable
@code {
private Timer? timer;
public void Dispose()
{
timer?.Dispose();
}
}

Performance Considerations

  • Blazor loads a ~2MB runtime on first use
  • The runtime is cached and reused across all Blazor components
  • Multiple components mode: All components share one runtime instance, minimizing overhead
  • Use the same Blazor app for multiple demos to avoid loading separate runtimes

Multiple Components

On Different Pages

You can show different components on different pages without creating multiple Blazor projects:

Page 1:

<div id="blazor-root" data-component="PhysicsDemo"></div>

Page 2:

<div id="blazor-root" data-component="WaveSimulation"></div>

On the Same Page

To display multiple Blazor components on a single page, use the data-components attribute with a JSON array:

<!-- Component containers -->
<div id="demo-1"></div>
<div id="demo-2"></div>
<!-- Blazor app configuration -->
<div id="blazor-root" data-components='[
{"name":"PhysicsDemo", "targetId":"demo-1"},
{"name":"WaveSimulation", "targetId":"demo-2"}
]'></div>
<script src="/blazor/_framework/blazor.webassembly.js" autostart="false"></script>
<script src="/blazor-init.js"></script>

This approach:

  • Uses only one Blazor runtime (~2MB) for all components
  • Allows multiple instances of the same component
  • Places each component at a specific location on the page
  • More efficient than loading multiple separate Blazor apps

Troubleshooting

Component Doesn’t Render

Check the browser console for errors. Common issues:

  • Component name misspelled in data-component
  • Component not registered in Home.razor
  • Missing Blazor scripts

Styles Not Applied

  • Ensure you created a .razor.css file with the same name as your component
  • Rebuild the Blazor project after adding new CSS files

Build Fails

  • Check that .NET 8 SDK is installed: dotnet --version
  • Verify the Blazor project builds independently: cd PAA.Blazor && dotnet build

Learn More

Example

See the working implementation at /demos/framework-integration which demonstrates both React and Blazor components working together.