Best Practices
Patterns, tips, and answers to common questions for building production-ready applications with NeoUI.
Start with Styled Components
Always reach for NeoUI.Blazor
styled components first. They have beautiful defaults, full theme support, and accessibility
built in. Only drop to the
Primitives layer
when you need complete control over markup and styling — for example, when building a bespoke
component that must match a custom design system that differs from shadcn/ui.
Primitives are low-level
Use the AsChild Pattern for Triggers
When you want a styled Button
to trigger an overlay, use AsChild
on the trigger component. This lets you compose any button variant without wrapping it in an
extra element or overriding default trigger styles:
This pattern applies to DialogTrigger,
PopoverTrigger,
SheetTrigger,
DropdownMenuTrigger, and all other trigger components.
Form Composition
Wrap related fields in FieldGroup
— it spaces out multiple fields with consistent vertical rhythm.
Wrap each individual control in Field
to compose the label, control, help text, and validation message as a single accessible unit.
Always set For on
FieldLabel to match the
Id of the focusable input
beneath it. This wires the HTML for
attribute correctly so clicking the label focuses the right control.
Set ShowValidationError="true"
on any input component to enable built-in
EditForm validation integration.
On submit, the component automatically applies error styling, the first invalid field is focused,
and a validation tooltip with the error message appears inline — no separate
FieldMessage required.
Use NeoUI inputs throughout
Input,
Select,
Combobox,
NumericInput,
Switch, and
Slider, to
advanced ones like MaskedInput,
DatePicker,
DateRangePicker,
ColorPicker, and
InputOTP.
Prefer these over native HTML inputs everywhere for consistent theming, dark mode support,
and built-in validation integration.
Theme Management in Production
- Ship only the theme files you use. If your app uses a fixed theme, include only one base and one primary CSS file. This avoids loading 22 theme files for users who cannot switch.
-
Always load theme CSS before
components.css. NeoUI reads the CSS variables at component render time. If variables aren't defined yet the components fall back to browser defaults. -
Include
theme.jsin<head>, not deferred. The script applies the saved theme class before paint to prevent a flash of unstyled content. -
Use
@Assets[...]for all static web asset references inApp.razor. .NET 10 adds a fingerprint hash for cache busting on each deployment.
Don't Fight the Defaults
NeoUI components come with carefully considered defaults for accessibility, focus management, and keyboard behavior. Before adding custom JavaScript or overriding ARIA attributes, check whether the component already supports the behavior you need — it often does via a parameter. Overriding built-in accessibility handling is the most common source of regressions.
Avoid suppressing focus rings
outline
globally breaks keyboard navigation for all users.
Use a Single Icon Library
All three icon libraries are included transitively with NeoUI.Blazor,
but pick one and use it consistently throughout your application. Mixing Lucide, Heroicons, and
Feather in the same UI creates visual inconsistency — different stroke weights, viewBox sizes,
and design languages. NeoUI itself uses Lucide everywhere.
FAQ
Traditional Blazor component libraries have their own proprietary design language. Learning MudBlazor means learning MudBlazor's component API and theming system, which does not transfer to other ecosystems.
NeoUI is aligned with the shadcn/ui design system. This means: themes from ui.shadcn.com and tweakcn.com work directly; developers coming from React/shadcn/ui find the API immediately familiar; and the headless Primitives layer provides complete styling freedom. NeoUI also ships pre-built CSS in the NuGet package — there is no theme builder, runtime CSS generation, or Sass compilation step.
components.css
ships inside the NuGet package and covers all component styling.
Tailwind is optional — useful if you want utility classes like
bg-primary or
text-muted-foreground
in your own layouts. See the
Theming guide
for the optional Tailwind v4 integration.
The styled components and primitives work in Blazor Hybrid via
BlazorWebView.
The portal hosts render correctly inside the WebView's component tree.
Some JS interop features (clipboard, keyboard shortcuts) may need platform-specific
adjustments on certain MAUI targets.
Beyond Blazor Hybrid, a dedicated NeoUI for MAUI library — native controls styled with the same shadcn/ui design language — is already on our roadmap. Watch the repository for updates.
wwwroot/styles/theme.css
and it works immediately. No conversion or mapping step is needed.
ThemeService
persists the selected theme in localStorage
so the theme is consistent across the transition.
ContainerPortalHost
handles lightweight, frequently-toggled overlays (Popover, Tooltip, DropdownMenu).
OverlayPortalHost
handles heavyweight full-screen overlays (Dialog, Sheet, Drawer). Separating them
means that hovering over tooltips or opening dropdowns does not trigger a re-render
of the dialog host — and vice versa. Dialogs with complex content don't cascade
re-renders into the rest of the overlay system.
BlazorUI.*
to NeoUI.* in version 1.0.7.
Update your NuGet references to the new package IDs and update
_content/BlazorUI.Components/
paths in App.razor
to _content/NeoUI.Blazor/.
All component APIs are backward-compatible with 1.0.x.