Architecture

NeoUI is built on a two-layer architecture that separates behavior from styling, and is designed first for .NET 10's Auto rendering mode.

Two-Layer Overview

NeoUI.Blazor

Styled components layer

100+ production-ready components with shadcn/ui design.

Pre-built CSS included. Built on top of the primitives layer.

Full theme support via CSS custom properties.

NeoUI.Blazor.Primitives

Headless accessibility layer

15 headless, unstyled components providing behavior only.

WCAG 2.1 AA accessibility, keyboard navigation, ARIA.

Bring your own styles — or use NeoUI.Blazor on top.

Every styled component in NeoUI.Blazor is built on a matching primitive. This means accessibility is never an afterthought — it is baked in at the foundation and the styled layer cannot regress it.

Auto Rendering Mode

All NeoUI components support .NET 10's Auto rendering mode, which starts with interactive Server-Side Rendering for fast first load, then seamlessly transitions to WebAssembly once the runtime has been downloaded in the background.

RZApp.razor
<!-- App.razor — enable Auto rendering mode -->
<HeadOutlet @rendermode="InteractiveAuto" />
<Routes @rendermode="InteractiveAuto" />

Fast initial load

Server renders the first frame immediately — no WASM download delay.

Rich interactivity

After WASM downloads, the app runs entirely client-side.

Seamless transition

Switching between modes is transparent to the user and to your components.

Portal System

Overlay components (Dialog, Sheet, Drawer, Popover, Tooltip, DropdownMenu) render their content outside the component tree using a portal. This prevents common CSS stacking context issues where a parent with overflow: hidden or a low z-index would clip the overlay.

NeoUI uses four dedicated hosts that you add to the end of your main layout:

RZMainLayout.razor
@inherits LayoutComponentBase

<div class="min-h-screen bg-background">
    @Body
</div>

@* All four hosts must be outside any scrollable or clipping container *@
<ToastViewport />
<DialogHost />
<ContainerPortalHost />
<OverlayPortalHost />

ToastViewport

Renders toast notification messages in a configurable screen position (e.g. BottomRight, TopCenter).

DialogHost

Required when using DialogService for programmatic dialogs — a convenience API to show dialogs from C# without adding <Dialog> markup to every page.

ContainerPortalHost

Inline overlays: Popover, Tooltip, DropdownMenu, ContextMenu, HoverCard. Positioned relative to their trigger.

OverlayPortalHost

Full-screen overlays: Dialog, AlertDialog, Sheet, Drawer. Rendered above everything with a backdrop.

CSS Variable Theming

Every color in every component is driven by a CSS custom property. There is no hardcoded palette. This means:

  • Themes switch by updating variable values — no class changes, no component re-renders
  • Full shadcn/ui and tweakcn.com theme compatibility
  • Dark mode by adding .dark to the <html> element
  • Instant runtime theme switching via ThemeService

See the Theming guide for the full variable reference.

AsChild Pattern

Trigger components (e.g. DialogTrigger, PopoverTrigger) accept an AsChild property. When set, the trigger renders its child element directly instead of its own default <button> — allowing you to use any styled component as the trigger without losing the overlay behavior.

RZ
<Dialog>
    @* AsChild: renders the Button, not a default <button> *@
    <DialogTrigger AsChild>
        <Button Variant="ButtonVariant.Outline">Open</Button>
    </DialogTrigger>
    <DialogContent>
        <DialogHeader>
            <DialogTitle>Hello from NeoUI</DialogTitle>
        </DialogHeader>
        <DialogFooter>
            <DialogClose AsChild>
                <Button Variant="ButtonVariant.Ghost">Close</Button>
            </DialogClose>
        </DialogFooter>
    </DialogContent>
</Dialog>

This is the standard pattern from Radix UI and shadcn/ui and enables full compositional flexibility.

Performance by Design

Performance is a first-class design constraint in NeoUI, not an afterthought. While NeoUI is fundamentally Pure Blazor — no JavaScript framework, no React, no Vue — certain behaviors are deliberately handed off to lightweight JavaScript helpers when doing so produces a measurably better user experience.

Keyboard navigation via JS

Keyboard interactions in interactive components (DropdownMenu, Select, Command, Combobox) are handled directly in JS rather than routing each keystroke through Blazor's event loop. This gives sub-frame response times — pressing or produces immediate visual feedback before the Blazor render cycle completes.

Consistent input experiences across rendering modes

Input components that require precise cursor control — masked inputs, OTP slots, numeric formatters — use JS-backed event interception so text composition (IME), cursor positioning, and mobile keyboard behavior work correctly regardless of whether the component is running on Blazor Server, WebAssembly, or in Auto mode. Without this, IME-heavy scripts (CJK) and mobile soft keyboards produce inconsistent behavior across rendering modes.

Selective re-renders via split portal hosts

ContainerPortalHost and OverlayPortalHost are deliberately separate components. Opening a tooltip or popover only triggers a re-render of ContainerPortalHost, not the heavier overlay host. Heavy dialog renders don't cascade into lightweight overlay interactions.

Intentional Motion

NeoUI's animations are designed to be felt rather than noticed. Every motion is purposeful, short-lived, and grounded in physical feedback — enhancing perceived quality without drawing attention to itself.

All animations are CSS-based: @keyframes compiled into components.css, combined with Tailwind animation utilities and CSS custom properties for duration and easing. No JavaScript animation library is required.

Accordion & Collapsible

Height animates using CSS custom properties (--radix-accordion-content-height). The HeightAnimation utility component generalises this pattern for any dynamic-height content.

Popover, Tooltip & DropdownMenu

Fade and scale from the anchor point using animate-in / animate-out Tailwind classes. Enter and exit use separate keyframes for asymmetric timing.

Dialog, Sheet & Drawer

Slide in from the appropriate edge or center on open; slide out on close. The backdrop fades independently from the panel so each can have its own easing.

Motion component

A declarative animation system with 20+ presets (fade, slide, scale, blur) for animating any element in your own pages and layouts, not just NeoUI components.

Key Design Principles

Accessibility first

WCAG 2.1 AA compliance baked into the primitives layer. Keyboard navigation and ARIA on every interactive component.

Zero configuration

Pre-built CSS ships with the NuGet package. No Tailwind, no Node.js, no build pipeline needed in the consuming project.

Composition over inheritance

Components are designed to be composed together. The AsChild pattern and render fragment slots make this natural.

Pure Blazor

No JavaScript framework dependencies. The small JS helpers (theme init, clipboard) are plain vanilla JS.

Feature-based organisation

Each component lives in its own folder with markup (.razor) and logic (.razor.cs) co-located.

Flat namespace

All components live in NeoUI.Blazor. One using directive gives you everything — no per-component imports.

Next Steps

Reconnecting...

Attempting to rejoin the server

Connection Lost

Retrying in seconds

Connection Failed

Failed to rejoin the server.
Please retry or reload the page.

Session Paused

The session has been paused by the server

Resume Failed

Failed to resume the session.
Please reload the page.