Tombatron.Turbo
1.0.0-alpha.2
See the version list below for details.
dotnet add package Tombatron.Turbo --version 1.0.0-alpha.2
NuGet\Install-Package Tombatron.Turbo -Version 1.0.0-alpha.2
<PackageReference Include="Tombatron.Turbo" Version="1.0.0-alpha.2" />
<PackageVersion Include="Tombatron.Turbo" Version="1.0.0-alpha.2" />
<PackageReference Include="Tombatron.Turbo" />
paket add Tombatron.Turbo --version 1.0.0-alpha.2
#r "nuget: Tombatron.Turbo, 1.0.0-alpha.2"
#:package Tombatron.Turbo@1.0.0-alpha.2
#addin nuget:?package=Tombatron.Turbo&version=1.0.0-alpha.2&prerelease
#tool nuget:?package=Tombatron.Turbo&version=1.0.0-alpha.2&prerelease
Tombatron.Turbo
Hotwire Turbo for ASP.NET Core with SignalR-powered real-time streams.
Features
- Turbo Frames: Partial page updates using manual partials
- Turbo Streams: Real-time updates via SignalR
- Simple Architecture: Check for
Turbo-Frameheader, return partial or redirect - Zero JavaScript Configuration: Works out of the box with Turbo.js
Installation
NuGet (ASP.NET Core server package):
dotnet add package Tombatron.Turbo
npm (JavaScript client library):
npm install @tombatron/turbo-signalr
Quick Start
1. Add Turbo Services
// Program.cs
builder.Services.AddTurbo();
2. Use Turbo Middleware
// Program.cs
app.UseRouting();
app.UseTurbo();
app.MapRazorPages();
app.MapTurboHub(); // For Turbo Streams
3. Add Tag Helpers
@* _ViewImports.cshtml *@
@addTagHelper *, Tombatron.Turbo
4. Create a Turbo Frame with a Partial
Create a partial view for your frame content:
<turbo-frame id="cart-items">
@foreach (var item in Model.Items)
{
<div>@item.Name - @item.Price</div>
}
</turbo-frame>
Use the partial in your page:
<h1>Shopping Cart</h1>
<partial name="_CartItems" model="Model" />
5. Handle Frame Requests in Your Page Model
public class CartModel : PageModel
{
public List<CartItem> Items { get; set; }
public void OnGet()
{
Items = GetCartItems();
}
public IActionResult OnGetRefresh()
{
Items = GetCartItems();
// For Turbo-Frame requests, return just the partial
if (HttpContext.IsTurboFrameRequest())
{
return Partial("_CartItems", this);
}
// For regular requests, redirect to the full page
return RedirectToPage();
}
}
6. Link to the Handler
<turbo-frame id="cart-items" src="/Cart?handler=Refresh">
Loading...
</turbo-frame>
<a href="/Cart?handler=Refresh" data-turbo-frame="cart-items">
Refresh Cart
</a>
Turbo Streams (Real-Time Updates)
Broadcast Updates from Server
public class CartController : Controller
{
private readonly ITurbo _turbo;
public CartController(ITurbo turbo)
{
_turbo = turbo;
}
[HttpPost]
public async Task<IActionResult> AddItem(int itemId)
{
// Add item to cart...
// Broadcast update to the user's stream
await _turbo.Stream($"user:{User.Identity.Name}", builder =>
{
builder.Update("cart-total", $"<span>${cart.Total}</span>");
});
return Ok();
}
}
Include the Client Script
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8/dist/turbo.es2017-esm.min.js"></script>
<script src="_content/Tombatron.Turbo/dist/turbo-signalr.bundled.min.js"></script>
Subscribe to Streams in Your View
<turbo stream="notifications"></turbo>
<turbo-stream-source-signalr stream="user:@User.Identity.Name" hub-url="/turbo-hub">
</turbo-stream-source-signalr>
<div id="cart-total">$0.00</div>
Stream Actions
await _turbo.Stream("notifications", builder =>
{
builder
.Append("list", "<div>New item</div>") // Add to end
.Prepend("list", "<div>First</div>") // Add to beginning
.Replace("item-1", "<div>Updated</div>") // Replace element
.Update("count", "42") // Update inner content
.Remove("old-item") // Remove element
.Before("btn", "<div>Before</div>") // Insert before
.After("btn", "<div>After</div>"); // Insert after
});
Configuration
builder.Services.AddTurbo(options =>
{
options.HubPath = "/turbo-hub";
options.UseSignedStreamNames = true;
options.AddVaryHeader = true;
});
Helper Extensions
Check if a request is a Turbo Frame request:
if (HttpContext.IsTurboFrameRequest())
{
return Partial("_MyPartial", Model);
}
// Or check for a specific frame
if (HttpContext.IsTurboFrameRequest("cart-items"))
{
return Partial("_CartItems", Model);
}
// Or check for a prefix (dynamic IDs)
if (HttpContext.IsTurboFrameRequestWithPrefix("item_"))
{
return Partial("_CartItem", Model);
}
How It Works
- User clicks a link or submits a form targeting a
<turbo-frame> - Turbo.js sends a request with the
Turbo-Frameheader - Your page handler checks for the header and returns a partial view
- Turbo.js extracts the matching frame from the response and updates the DOM
This approach is simple, explicit, and gives you full control over what content is returned.
Documentation
Guides
- Turbo Frames Guide - Partial page updates
- Turbo Streams Guide - Real-time updates
- Authorization Guide - Securing streams
- Testing Guide - Testing strategies
- Troubleshooting - Common issues
API Reference
- ITurbo - Main service interface
- ITurboStreamBuilder - Stream builder
- TurboOptions - Configuration
- Tag Helpers - Razor tag helpers
Migration Guides
Sample Application
The repository includes a sample application demonstrating:
- Turbo Frames for partial updates
- Turbo Streams for real-time features
- Shopping cart with add/remove operations
- Live notifications and counters
Run the sample:
cd samples/Tombatron.Turbo.Sample
dotnet run
Requirements
- .NET 9.0 or later
- ASP.NET Core
- Turbo.js 8.x (client-side)
- SignalR (for Turbo Streams)
Publishing / Releases
Both the NuGet and npm packages are published automatically when a version tag is pushed:
git tag v1.2.3
git push origin v1.2.3
This triggers the Release workflow which builds, tests, and publishes:
The npm package can also be published independently via the manual workflow.
License
MIT License - see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. 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. |
-
net9.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Tombatron.Turbo:
| Package | Downloads |
|---|---|
|
Tombatron.Turbo.Stimulus
Stimulus controller support for Tombatron.Turbo. Runtime controller discovery, dynamic import map generation, and automatic integration with turbo-scripts. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 102 | 3/7/2026 |
| 1.0.0-alpha.11 | 49 | 3/5/2026 |
| 1.0.0-alpha.10 | 53 | 2/25/2026 |
| 1.0.0-alpha.9 | 47 | 2/24/2026 |
| 1.0.0-alpha.8 | 51 | 2/22/2026 |
| 1.0.0-alpha.7 | 54 | 2/22/2026 |
| 1.0.0-alpha.6 | 56 | 2/21/2026 |
| 1.0.0-alpha.5 | 57 | 2/18/2026 |
| 1.0.0-alpha.4 | 57 | 2/17/2026 |
| 1.0.0-alpha.3 | 55 | 2/15/2026 |
| 1.0.0-alpha.2 | 52 | 2/15/2026 |
| 1.0.0-alpha.1 | 55 | 2/15/2026 |