BlazorBlueprint.Primitives
3.16.0
dotnet add package BlazorBlueprint.Primitives --version 3.16.0
NuGet\Install-Package BlazorBlueprint.Primitives -Version 3.16.0
<PackageReference Include="BlazorBlueprint.Primitives" Version="3.16.0" />
<PackageVersion Include="BlazorBlueprint.Primitives" Version="3.16.0" />
<PackageReference Include="BlazorBlueprint.Primitives" />
paket add BlazorBlueprint.Primitives --version 3.16.0
#r "nuget: BlazorBlueprint.Primitives, 3.16.0"
#:package BlazorBlueprint.Primitives@3.16.0
#addin nuget:?package=BlazorBlueprint.Primitives&version=3.16.0
#tool nuget:?package=BlazorBlueprint.Primitives&version=3.16.0
BlazorBlueprint.Primitives
Headless, unstyled Blazor primitive components with ARIA attributes and keyboard support. Build your own component library using these composable primitives.
Features
- Headless & Unstyled: Complete control over styling — primitives provide behavior, accessibility, and state management without imposing any visual design
- Built with Accessibility in Mind: Includes ARIA attributes and keyboard interaction support
- Composition-Based: Flexible component composition patterns for building complex UIs
- Type-Safe: Full C# type safety with IntelliSense support
- State Management: Built-in controlled and uncontrolled state patterns
- Keyboard Support: Keyboard interaction support for interactive components
- Two-Layer Portal Architecture: Category-scoped portals (Container and Overlay) for efficient rendering
- .NET 8: Built for the latest .NET platform
Installation
dotnet add package BlazorBlueprint.Primitives
Setup
Register services in Program.cs:
builder.Services.AddBlazorBlueprintPrimitives();
Add the portal host to your root layout (MainLayout.razor):
<BbPortalHost />
Add a single import to _Imports.razor:
@using BlazorBlueprint.Primitives
Available Primitives
| Primitive | Description |
|---|---|
| Accordion | Collapsible content sections with single or multiple item expansion |
| Alert Dialog | Modal requiring explicit acknowledgement, no dismiss via overlay or Escape |
| Checkbox | Binary selection control with indeterminate state and BbCheckboxIndicator sub-component |
| Collapsible | Expandable content area with trigger control |
| Context Menu | Right-click menu with keyboard navigation and positioning |
| Dashboard Grid | Widget layout state, drag-and-drop coordination, resize handling, responsive breakpoints |
| DataGrid | Headless data grid with sorting, filtering, pagination, selection, expansion, row grouping, and state management |
| Dialog | Modal dialogs with backdrop, focus management, and portal rendering |
| Dropdown Menu | Context menus with items, checkbox items, separators, and keyboard shortcuts |
| Hover Card | Rich preview cards on hover with delay control |
| Label | Accessible labels for form controls with automatic association |
| Popover | Floating panels for additional content with positioning |
| Progress | Accessible progress bar with determinate and indeterminate states |
| Radio Group | Mutually exclusive options with keyboard navigation |
| Scroll Area | Custom scrollbar with accessible ARIA scrollbar role and drag support |
| Select | Dropdown selection with cascading type inference and display text resolution |
| Separator | Semantic or decorative divider with orientation support |
| Sheet | Side panels that slide in from viewport edges |
| Slider | Range input with keyboard navigation and pointer drag support |
| Switch | Toggle control with BbSwitchThumb sub-component for automatic data-state sync |
| Table | Data table with header, body, rows, cells, and pagination |
| Tabs | Tabbed interface with keyboard navigation |
| Toggle | Pressed/active state with aria-pressed support |
| Tooltip | Brief informational popups with hover/focus triggers |
| Tree View | Hierarchical expand/collapse, selection, and checkbox state management |
Services
| Service | Description |
|---|---|
IPortalService |
Two-layer portal management with Container and Overlay categories |
IFocusManager |
Focus trapping and restoration for overlays |
IPositioningService |
Floating UI positioning with auto-update |
IKeyboardShortcutService |
Global keyboard shortcut registration and management |
DropdownManagerService |
Coordinates open/close state across multiple dropdowns |
API Reference
Accordion
<BbAccordion Type="AccordionType.Single" Collapsible="true" DefaultValue="item-1">
<BbAccordionItem Value="item-1">
<BbAccordionTrigger>Section 1</BbAccordionTrigger>
<BbAccordionContent>Content 1</BbAccordionContent>
</BbAccordionItem>
</BbAccordion>
| Parameter | Type | Default | Description |
|---|---|---|---|
Type |
AccordionType |
Single |
Single (one item open) or Multiple (many items open) |
Collapsible |
bool |
false |
When Single, allows closing all items |
Checkbox
<BbCheckbox @bind-Checked="isChecked" Indeterminate="@isIndeterminate">
<BbCheckboxIndicator />
</BbCheckbox>
| Parameter | Type | Default | Description |
|---|---|---|---|
Checked |
bool |
false |
Checked state |
Indeterminate |
bool |
false |
Shows partial/mixed state |
BbCheckboxIndicator renders the appropriate check or indeterminate SVG icon automatically based on parent state:
| Parameter | Type | Default | Description |
|---|---|---|---|
ChildContent |
RenderFragment? |
null |
Custom content instead of default icons |
Size |
int |
14 |
SVG icon size in pixels |
StrokeWidth |
int |
3 |
SVG stroke width |
Select
<BbSelect TValue="string" @bind-Value="selected" @bind-Open="isOpen">
<BbSelectTrigger>
<BbSelectValue Placeholder="Choose..." />
</BbSelectTrigger>
<BbSelectContent>
<BbSelectItem Value="@("a")" Text="Option A" />
<BbSelectItem Value="@("b")" Text="Option B" />
</BbSelectContent>
</BbSelect>
Select uses [CascadingTypeParameter] — child components infer TValue from the parent. Supports ItemClass for parent-level item styling.
| Parameter | Type | Default | Description |
|---|---|---|---|
Value |
TValue? |
— | Selected value (two-way bindable) |
Open |
bool |
false |
Open state (two-way bindable) |
ItemClass |
string? |
null |
CSS classes cascaded to all BbSelectItem children |
Dialog
<BbDialog @bind-Open="isOpen">
<BbDialogTrigger>Open</BbDialogTrigger>
<BbDialogPortal>
<BbDialogOverlay />
<BbDialogContent>
<BbDialogTitle>Title</BbDialogTitle>
<BbDialogDescription>Description</BbDialogDescription>
<BbDialogClose>Close</BbDialogClose>
</BbDialogContent>
</BbDialogPortal>
</BbDialog>
Sheet
<BbSheet>
<BbSheetTrigger>Open</BbSheetTrigger>
<BbSheetPortal>
<BbSheetOverlay />
<BbSheetContent Side="SheetSide.Right">
<BbSheetTitle>Title</BbSheetTitle>
<BbSheetDescription>Description</BbSheetDescription>
<BbSheetClose>Close</BbSheetClose>
</BbSheetContent>
</BbSheetPortal>
</BbSheet>
| Parameter | Type | Default | Description |
|---|---|---|---|
Side |
SheetSide |
Right |
Top, Right, Bottom, Left |
Popover
<BbPopover>
<BbPopoverTrigger>Open</BbPopoverTrigger>
<BbPopoverContent Side="PopoverSide.Bottom" Align="PopoverAlign.Center">
Content here
</BbPopoverContent>
</BbPopover>
| Parameter | Type | Default | Description |
|---|---|---|---|
Side |
PopoverSide |
Bottom |
Top, Right, Bottom, Left |
Align |
PopoverAlign |
Center |
Start, Center, End |
CloseOnEscape |
bool |
true |
Close when Escape key pressed |
CloseOnClickOutside |
bool |
true |
Close when clicking outside |
Tooltip
<BbTooltip DelayDuration="700" HideDelay="0">
<BbTooltipTrigger>Hover me</BbTooltipTrigger>
<BbTooltipContent>Tooltip text</BbTooltipContent>
</BbTooltip>
| Parameter | Type | Default | Description |
|---|---|---|---|
DelayDuration |
int |
700 |
Milliseconds before showing |
HideDelay |
int |
0 |
Milliseconds before hiding |
HoverCard
<BbHoverCard OpenDelay="700" CloseDelay="300">
<BbHoverCardTrigger>Hover for preview</BbHoverCardTrigger>
<BbHoverCardContent>Rich preview content</BbHoverCardContent>
</BbHoverCard>
| Parameter | Type | Default | Description |
|---|---|---|---|
OpenDelay |
int |
700 |
Milliseconds before showing |
CloseDelay |
int |
300 |
Milliseconds before hiding |
Dropdown Menu
<BbDropdownMenu ItemClass="px-2 py-1.5 cursor-pointer rounded hover:bg-accent">
<BbDropdownMenuTrigger>Menu</BbDropdownMenuTrigger>
<BbDropdownMenuContent>
<BbDropdownMenuItem>Cut</BbDropdownMenuItem>
<BbDropdownMenuItem>Copy</BbDropdownMenuItem>
<BbDropdownMenuItem Href="https://example.com" Target="_blank">Visit Site</BbDropdownMenuItem>
<BbDropdownMenuCheckboxItem @bind-Checked="isEnabled">Enable</BbDropdownMenuCheckboxItem>
</BbDropdownMenuContent>
</BbDropdownMenu>
| Parameter | Type | Default | Description |
|---|---|---|---|
ItemClass |
string? |
null |
CSS classes cascaded to all menu items |
BbDropdownMenuItem supports Href and Target for link items — renders as <a> when Href is set.
Switch
<BbSwitch @bind-Checked="isEnabled" class="relative h-6 w-11 rounded-full bg-input">
<BbSwitchThumb class="pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg" />
</BbSwitch>
BbSwitchThumb automatically syncs data-state ("checked" / "unchecked") from the parent via cascading parameter.
Radio Group
<BbRadioGroup TValue="string" @bind-Value="selected" ItemClass="flex items-center gap-2">
<BbRadioGroupItem Value="@("a")">Option A</BbRadioGroupItem>
<BbRadioGroupItem Value="@("b")">Option B</BbRadioGroupItem>
</BbRadioGroup>
| Parameter | Type | Default | Description |
|---|---|---|---|
ItemClass |
string? |
null |
CSS classes cascaded to all radio items |
Tabs
<BbTabs DefaultValue="tab1" Orientation="TabsOrientation.Horizontal"
ActivationMode="TabsActivationMode.Automatic">
<BbTabsList>
<BbTabsTrigger Value="tab1">Tab 1</BbTabsTrigger>
</BbTabsList>
<BbTabsContent Value="tab1">Content</BbTabsContent>
</BbTabs>
| Parameter | Type | Default | Description |
|---|---|---|---|
Orientation |
TabsOrientation |
Horizontal |
Horizontal, Vertical |
ActivationMode |
TabsActivationMode |
Automatic |
Automatic (on focus), Manual (on click) |
Table
<BbTable TData="Person">
<BbTableHeader>
<BbTableRow>
<BbTableHeaderCell>Name</BbTableHeaderCell>
<BbTableHeaderCell>Email</BbTableHeaderCell>
</BbTableRow>
</BbTableHeader>
<BbTableBody>
@foreach (var person in people)
{
<BbTableRow>
<BbTableCell>@person.Name</BbTableCell>
<BbTableCell>@person.Email</BbTableCell>
</BbTableRow>
}
</BbTableBody>
</BbTable>
| Parameter | Type | Default | Description |
|---|---|---|---|
SelectionMode |
SelectionMode |
None |
None, Single, Multiple |
SortDirection |
SortDirection |
None |
None, Ascending, Descending |
Portal Architecture
Primitives use a two-layer portal system for rendering overlay content:
- Container portals (
PortalCategory.Container): Dialog, Sheet — full-screen overlays - Overlay portals (
PortalCategory.Overlay): Popover, Select, Dropdown, Tooltip, HoverCard — positioned floating content
Each category has its own host (BbContainerPortalHost, BbOverlayPortalHost), so opening a tooltip doesn't cause Dialog portals to re-render. BbPortalHost is a convenience wrapper that renders both.
BbFloatingPortal keeps content mounted in the DOM when closed (ForceMount defaults to true), hidden via CSS. A data-state attribute ("open" / "closed") on the portal content enables CSS animations.
Controlled vs Uncontrolled
All stateful primitives support both controlled and uncontrolled modes:
Uncontrolled (Component manages its own state)
<BbDialog>
<BbDialogTrigger>Open</BbDialogTrigger>
<BbDialogPortal>
<BbDialogOverlay />
<BbDialogContent>Content</BbDialogContent>
</BbDialogPortal>
</BbDialog>
Controlled (Parent component manages state)
<BbDialog @bind-Open="isDialogOpen">
<BbDialogTrigger>Open</BbDialogTrigger>
<BbDialogPortal>
<BbDialogOverlay />
<BbDialogContent>
<button @onclick="() => isDialogOpen = false">Close</button>
</BbDialogContent>
</BbDialogPortal>
</BbDialog>
@code {
private bool isDialogOpen = false;
}
Design Philosophy
BlazorBlueprint.Primitives follows the "headless component" pattern popularized by Radix UI and Headless UI:
- Separation of Concerns: Primitives handle behavior and accessibility; you handle the design
- Composability: Build complex components by composing simple primitives
- No Style Opinions: Zero CSS included — bring your own design system
- Accessibility by Default: ARIA attributes and keyboard navigation built-in
When to Use
Use BlazorBlueprint.Primitives when:
- Building a custom design system from scratch
- Need complete control over component styling
- Want to match a specific brand or design language
- Integrating with existing CSS frameworks or design tokens
Consider BlazorBlueprint.Components when:
- Want beautiful defaults with shadcn/ui design
- Prefer zero-configuration setup with pre-built CSS
- Need to ship quickly without custom styling
Documentation
For full documentation, examples, and API reference, visit:
License
Apache License 2.0 - see LICENSE for details.
Contributing
Contributions are welcome! Please see our Contributing Guide.
| Product | Versions 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. |
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.14)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on BlazorBlueprint.Primitives:
| Package | Downloads |
|---|---|
|
BlazorBlueprint.Components
Pre-styled Blazor components built with shadcn/ui design and Tailwind CSS. Beautiful defaults that you can customize to match your brand. |
|
|
BlueprintShell
Embeddable Blazor shell built on BlazorBlueprint. Spin up a themed, dockable UI on a configurable port from any .NET application. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.16.0 | 518 | 9/4/2026 |
| 3.15.0 | 16,329 | 8/5/2026 |
| 3.14.1 | 16,540 | 7/16/2026 |
| 3.14.0 | 820 | 7/15/2026 |
| 3.13.0 | 10,657 | 7/2/2026 |
| 3.12.0 | 10,300 | 6/14/2026 |
| 3.11.0 | 5,043 | 6/8/2026 |
| 3.10.2 | 11,627 | 5/14/2026 |
| 3.10.1 | 164 | 5/14/2026 |
| 3.10.0 | 1,141 | 5/2/2026 |
| 3.9.3 | 10,261 | 4/1/2026 |
| 3.9.2 | 1,281 | 3/26/2026 |
| 3.7.3 | 1,541 | 3/22/2026 |
| 3.7.2 | 147 | 3/22/2026 |
| 3.7.1 | 942 | 3/21/2026 |
| 3.7.0 | 158 | 3/21/2026 |
| 3.6.1 | 399 | 3/20/2026 |
## What's New in v3.16.0
### New Features
- **DataGrid** — `IDataGridColumn` gains `GetSortAndFilterValue` and `GetSortAndFilterExpression`, so a column can sort, filter, search, group and export on a projected value rather than the one it displays. Both have default implementations, so existing column types are unaffected.
- **DataGrid** — new `DataGridSelectionBehavior` with `Toggle` (the default) and `Replace`; `Replace` gives plain-click, Shift+Click range and Ctrl/Cmd+Click semantics. Exposed as `SelectionBehavior` on `BbDataGrid`, `BbDataGridRow` and `DataGridContext`.
- **SelectionState** — new `Anchor`, `ReplaceWith` and `SelectRange`, with `DataGridContext.ApplyRowSelectionInput` and the `RowSelectionModifiers` struct resolving a click into a selection change.
- **DataGrid** — new `DataGridCsvWriter` writes the current rows to CSV in visible column order, using each column's display text. A cell starting `=`, `+`, `-`, `@`, tab or CR is escaped so a spreadsheet does not run it as a formula.
- **DataGrid** — grouping now nests to any depth. `DataGridGroupState` gains `ActiveGroups`, `Depth`, `SetGroups`, `AddGroup`, `RemoveGroup`, `MoveGroup`, `GetLevel`, `IsGroupedBy`, `CollapsedPaths` and `IsHiddenByCollapse`; the single-level members are kept and still work.
- **GroupPath** — new type keying collapsed state by the full ancestor chain, because a raw key is ambiguous once grouping nests.
- **DataGridGroupRow** — new `Path`, `Depth` and `Children`; `Items` now carries every row beneath the group so aggregates roll up.
- **DataGrid** — new `DataGridEditMode` and row editing hooks `IsRowEditing`, `OnCommitEdit` and `OnCancelEdit` on `BbDataGrid` and `BbDataGridRow`, plus `IDataGridColumn.EditTemplate`.
- **DataGridRowSnapshot** — new type restoring a row's original values in place when an edit is cancelled, so anything else holding that row sees the revert.
- **DataGridRenderItem** — new `ForGroupedData(item, depth)` carries the indent depth for a row nested under group headers.
- **DataGridGroupedResult**, **DataGridStateSnapshot** — new `GroupDefinitions` carries the full ordered level list for server-side grouping and state persistence.
- **Filtering** — new `FilterCondition.MatchesValue` and `ToExpressionForSelector`, so a filter can run against any expression instead of only a property resolved by name.
- **AlertDialog**, **AlertDialogPortal**, **TablePagination** — added `AdditionalAttributes`, so unmatched attributes splat onto the rendered element.
### Bug Fixes
- **Popover**, **DropdownMenu**, **HoverCard**, **Dialog**, **Sheet** — the subtree now repaints whenever context open state moves, not only when the consumer agrees. A consumer overriding `ShouldRender` could previously leave the comparison stale, after which `OpenChanged` silently stopped firing for good.
- **HoverCardContent**, **TooltipContent** — `data-state` is bound to the context instead of hardcoded to `open`, so the closed-state exit animation classes can match.
- **FloatingPortal** — the element is held positioned and visible between close and the end of the exit animation, so the animation is seen rather than hidden away immediately. Pointer events stop at once, a reopen cancels the pending hide, and a one-second cap stops a stalled animation stranding an overlay on screen.
- **DataGrid** — a sort key selector declared as `Expression<Func<TData, object>>` has its boxing `Convert` node stripped, so value types order correctly in memory and the sort is still translatable by an `IQueryable` provider.
- **DataGrid** — Enter and Escape now reach Blazor from an input inside a row being edited; the row's keydown interceptor previously blocked every handler in the row.
- **DataGrid** — the focused field is blurred before an Enter commit, so the value just typed is no longer dropped.
### Improvements
- **PortalService** — the missing-host warning now says a host is not registered in this render context, separates a genuinely missing host from a wrong-context one, and names both fixes. It no longer links to an unpublished guide page.
- **FloatingPortal** — the per-portal host timeout is logged only when a host is registered, where it means a slow render rather than a missing host. A page of tooltips produced 24 near-identical warnings and now produces one.