Getting Started

Learn how to use primitives and components in your Blazor application

Primitives vs Components

Primitives

Headless, unstyled components that provide behavior, accessibility, and state management.

Full control over styling
Accessibility built-in
Keyboard navigation
Use your own design system

Components

Pre-styled wrappers around primitives with shadcn/ui design and Tailwind classes.

Beautiful defaults
shadcn/ui styling
Quick to implement
Dark mode support

Usage Examples

Using a Component (Quick Start)

Import and use styled components directly with beautiful defaults:

@using BlazorUI.Components.Dialog

<Dialog>
    <DialogTrigger>Open Dialog</DialogTrigger>
    <DialogContent>
        <DialogHeader>
            <DialogTitle>Dialog Title</DialogTitle>
            <DialogDescription>Description text</DialogDescription>
        </DialogHeader>
        <p>Dialog content goes here</p>
    </DialogContent>
</Dialog>

Using a Primitive (Custom Styling)

Use primitives when you want full control over styling:

@using BlazorUI.Primitives.Dialog

<DialogPrimitive>
    <DialogTrigger class="my-custom-button-class">
        Open Dialog
    </DialogTrigger>
    <DialogPortal>
        <DialogOverlay class="my-overlay-styles" />
        <DialogContent class="my-custom-dialog-styles">
            <DialogTitle class="my-title-styles">
                Custom Styled Dialog
            </DialogTitle>
            <p class="my-content-styles">Fully custom!</p>
        </DialogContent>
    </DialogPortal>
</DialogPrimitive>

Composition Patterns

Components are built using composition - combine sub-components to create complex UIs.

Required Pattern

Most primitives require a specific component hierarchy:

<!-- Root component manages state -->
<DialogPrimitive>
    <!-- Trigger opens the dialog -->
    <DialogTrigger>...</DialogTrigger>

    <!-- Portal renders outside DOM hierarchy -->
    <DialogPortal>
        <!-- Overlay for click-outside behavior -->
        <DialogOverlay />

        <!-- Content with focus management -->
        <DialogContent>...</DialogContent>
    </DialogPortal>
</DialogPrimitive>

Controlled State

Control component state from parent using @bind-:

<DialogPrimitive @bind-Open="isDialogOpen">
    <DialogTrigger>Open</DialogTrigger>
    <DialogContent>
        <p>Controlled by parent component</p>
        <button @onclick="() => isDialogOpen = false">
            Close
        </button>
    </DialogContent>
</DialogPrimitive>

@code {
    private bool isDialogOpen = false;
}

Uncontrolled State

Let the component manage its own state:

<DialogPrimitive>
    <DialogTrigger>Open</DialogTrigger>
    <DialogContent>
        <p>Component manages its own open/close state</p>
        <DialogClose>Close</DialogClose>
    </DialogContent>
</DialogPrimitive>

Common Patterns

Cascading Context

Root components use CascadingValue to share state with child components. Child components automatically receive context - no prop drilling needed.

Portal Rendering

Components like Dialog and Popover use Portal to render outside their parent DOM to avoid z-index issues and enable proper overlays.

Event Callbacks

Components expose events like OpenChanged that you can hook into for custom logic when state changes.

Next Steps

An unhandled error has occurred. Reload 🗙