Headless, unstyled Blazor primitive components with full accessibility support. Build your own component library using these composable primitives.
$ dotnet add package NeoUI.Blazor.PrimitivesHeadless, unstyled Blazor primitive components with full accessibility support. Build your own component library using these composable primitives.
dotnet add package NeoUI.Blazor.Primitives
Try the Live Demo - Explore all primitives and styled components with interactive examples and full documentation.
Built on top of these primitives, NeoUI.Blazor provides production-ready components with beautiful shadcn/ui design:
Button, Button Group, Checkbox, Combobox, Date Picker, Field, Input, Input Group, Input OTP, Label, Multi Select, Native Select, Radio Group, Select, Slider, Switch, Textarea, Time Picker
Avatar, Badge, Card, Data Table, Empty, Grid, Item, Kbd, Progress, Separator, Skeleton, Spinner, Typography
Accordion, Breadcrumb, Command, Context Menu, Dropdown Menu, Menubar, Navigation Menu, Pagination, Sidebar, Tabs
Alert Dialog, Dialog, Hover Card, Popover, Sheet, Toast, Tooltip
Alert
Aspect Ratio, Carousel, Collapsible, Resizable, Scroll Area
Chart (8 types), Grid, Markdown Editor, Motion (20+ animation presets), Rich Text Editor, Toggle, Toggle Group
Lucide Icons (1,640+), Heroicons (1,288), Feather Icons (286)
Want beautiful defaults? Check out the Components README for full documentation.
<AccordionPrimitive Type="AccordionType.Single" Collapsible="true" DefaultValue="item-1">
<AccordionItemPrimitive Value="item-1">
<AccordionTriggerPrimitive>Section 1</AccordionTriggerPrimitive>
<AccordionContentPrimitive>Content 1</AccordionContentPrimitive>
</AccordionItemPrimitive>
</AccordionPrimitive>
| Parameter | Type | Default | Values |
|---|---|---|---|
Type | AccordionType | Single | Single (one item open), Multiple (many items open) |
Collapsible | bool | false | When Single, allows closing all items |
<TabsPrimitive
DefaultValue="tab1"
Orientation="TabsOrientation.Horizontal"
ActivationMode="TabsActivationMode.Automatic">
<TabsListPrimitive>
<TabsTriggerPrimitive Value="tab1">Tab 1</TabsTriggerPrimitive>
</TabsListPrimitive>
<TabsContentPrimitive Value="tab1">Content</TabsContentPrimitive>
</TabsPrimitive>
| Parameter | Type | Default | Values |
|---|---|---|---|
Orientation | TabsOrientation | Horizontal | Horizontal, Vertical |
ActivationMode | TabsActivationMode | Automatic | Automatic (on focus), Manual (on click) |
<SheetPrimitive>
<SheetTriggerPrimitive>Open</SheetTriggerPrimitive>
<SheetPortal>
<SheetOverlay />
<SheetContentPrimitive Side="SheetSide.Right">
<SheetTitlePrimitive>Title</SheetTitlePrimitive>
<SheetDescriptionPrimitive>Description</SheetDescriptionPrimitive>
<SheetClosePrimitive>Close</SheetClosePrimitive>
</SheetContentPrimitive>
</SheetPortal>
</SheetPrimitive>
| Parameter | Type | Default | Values |
|---|---|---|---|
Side | SheetSide | Right | Top, Right, Bottom, Left |
<PopoverPrimitive>
<PopoverTriggerPrimitive>Open</PopoverTriggerPrimitive>
<PopoverContentPrimitive Side="PopoverSide.Bottom" Align="PopoverAlign.Center">
Content here
</PopoverContentPrimitive>
</PopoverPrimitive>
| Parameter | Type | Default | Values |
|---|---|---|---|
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 |
<TooltipPrimitive DelayDuration="400" HideDelay="0">
<TooltipTriggerPrimitive>Hover me</TooltipTriggerPrimitive>
<TooltipContentPrimitive>Tooltip text</TooltipContentPrimitive>
</TooltipPrimitive>
| Parameter | Type | Default | Description |
|---|---|---|---|
DelayDuration | int | 400 | Milliseconds before showing |
HideDelay | int | 0 | Milliseconds before hiding |
<HoverCardPrimitive OpenDelay="700" CloseDelay="300">
<HoverCardTriggerPrimitive>Hover for preview</HoverCardTriggerPrimitive>
<HoverCardContentPrimitive>Rich preview content</HoverCardContentPrimitive>
</HoverCardPrimitive>
| Parameter | Type | Default | Description |
|---|---|---|---|
OpenDelay | int | 700 | Milliseconds before showing |
CloseDelay | int | 300 | Milliseconds before hiding |
<CheckboxPrimitive
@bind-Checked="isChecked"
Indeterminate="@isIndeterminate"
IndeterminateChanged="HandleIndeterminateChange" />
| Parameter | Type | Default | Description |
|---|---|---|---|
Checked | bool | false | Checked state |
Indeterminate | bool | false | Shows partial/mixed state |
<SelectPrimitive TValue="string" @bind-Value="selected" @bind-Open="isOpen">
<SelectTriggerPrimitive>
Choose an option...
</SelectTriggerPrimitive>
<SelectContentPrimitive>
<SelectItemPrimitive Value="@("a")">Option A</SelectItemPrimitive>
<SelectItemPrimitive Value="@("b")">Option B</SelectItemPrimitive>
</SelectContentPrimitive>
</SelectPrimitive>
Select is generic (TValue). Supports both value and open state binding.
<Table TData="Person">
<TableHeader>
<TableRow>
<TableHeaderCell>Name</TableHeaderCell>
<TableHeaderCell>Email</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
@foreach (var person in people)
{
<TableRow>
<TableCell>@person.Name</TableCell>
<TableCell>@person.Email</TableCell>
</TableRow>
}
</TableBody>
</Table>
| Parameter | Type | Default | Values |
|---|---|---|---|
SelectionMode | SelectionMode | None | None, Single, Multiple |
SortDirection | SortDirection | None | None, Ascending, Descending |
<MultiSelect TItem="string" @bind-SelectedItems="selected" Items="options">
<MultiSelectTrigger>
<MultiSelectInput Placeholder="Select items..." />
</MultiSelectTrigger>
<MultiSelectContent>
<MultiSelectSelectAll>Select All</MultiSelectSelectAll>
@foreach (var item in options)
{
<MultiSelectItem Value="item">@item</MultiSelectItem>
}
</MultiSelectContent>
</MultiSelect>
| Parameter | Type | Description |
|---|---|---|
SelectAllState | SelectAllState | None, Indeterminate, All |
@using NeoUI.Blazor.Primitives
<DialogPrimitive>
<DialogTriggerPrimitive class="my-custom-button-class">
Open Dialog
</DialogTriggerPrimitive>
<DialogPortal>
<DialogOverlay class="my-overlay-styles" />
<DialogContentPrimitive class="my-custom-dialog-styles">
<DialogTitlePrimitive class="my-title-styles">
Custom Styled Dialog
</DialogTitlePrimitive>
<DialogDescriptionPrimitive class="my-description-styles">
This is a fully customizable dialog.
</DialogDescriptionPrimitive>
<p class="my-content-styles">Your content here</p>
<DialogClosePrimitive class="my-close-button">Close</DialogClosePrimitive>
</DialogContentPrimitive>
</DialogPortal>
</DialogPrimitive>
All stateful primitives support both controlled and uncontrolled modes:
<DialogPrimitive>
<DialogTriggerPrimitive>Open</DialogTriggerPrimitive>
<DialogPortal>
<DialogOverlay />
<DialogContentPrimitive>
<!-- Component handles open/close internally -->
</DialogContentPrimitive>
</DialogPortal>
</DialogPrimitive>
<DialogPrimitive @bind-Open="isDialogOpen">
<DialogTriggerPrimitive>Open</DialogTriggerPrimitive>
<DialogPortal>
<DialogOverlay />
<DialogContentPrimitive>
<button @onclick="() => isDialogOpen = false">Close</button>
</DialogContentPrimitive>
</DialogPortal>
</DialogPrimitive>
@code {
private bool isDialogOpen = false;
}
NeoUI.Blazor.Primitives follows the "headless component" pattern popularized by Radix UI and Headless UI:
Use NeoUI.Blazor.Primitives when:
Consider NeoUI.Blazor when:
For full documentation, examples, and API reference, visit:
MIT License - see LICENSE file for details
Contributions are welcome! Please feel free to submit a Pull Request.