@nxgt/material-vue
v0.19.4
Published
Readme
@nxgt/material-vue
Vue 3 component library: primitives, form fields, layout shell, DataTable,
drag-and-drop, i18n and Icon. Built on Tailwind CSS v4 and shadcn-vue
(Reka UI), with Pinia for widget state and vee-validate for forms.
It is the Vue mirror of @nxgt/material
(React). Component names match wherever both libraries ship the same thing;
the catalogue lists the React equivalent for each entry.
At a glance
- 380+ exported components in 169 catalogue entries, grouped in 12 categories in the catalogue: from shadcn-vue primitives to kanban, event calendar, charts, documentation pages, rich-text editor, PDF reader and media player.
- Form fields, standalone or vee-validate. Each field works with
v-model, and most have a*FormFieldwrapper that binds to a vee-validate form byname. See Form fields. - Helpers:
cn,clientOnly, date, number and string utilities, Pinia store factories (defineLocalStore, …) and composables (useTheme,useIsMobile, …). See Helpers. - i18n built in:
I18nProvider, ICU messages, English and French strings for every component, and a way to merge in your own resources. - Nuxt module: auto-imports components and composables, and installs Pinia.
- SSR-safe barrels: browser-only widgets (PDF, Monaco, webcam, player) are lazy and client-only.
Contents
- Install
- Setup: styles, Pinia and router, icons, Nuxt
- Subpaths
- Usage
- Components
- Helpers
- SSR
- Traps
- Development
- License
Install
bun add @nxgt/material-vue
bun add vue vue-router pinia vee-validate @tanstack/vue-table @vueuse/coreThe package is public on npmjs, so no token is needed. The peers are required: the app must resolve a single copy of each, otherwise injections and stores silently split between two instances.
| Peer | Range | Used by |
| --- | --- | --- |
| vue | ^3.5 | everything |
| pinia | ^3 | widget state (filter, gallery, kanban, calendar, player, upload, …) |
| vue-router | ^4.5 \|\| ^5 | app shell, LinkButton, useSearchParam, useNavigationHandler |
| vee-validate | ^4.15 | every *FormField and the built-in forms (attributes, contacts, …) |
| @tanstack/vue-table | ^9 | DataTable |
| @vueuse/core | ^13 \|\| ^14 | useIsMobile, useTheme, ScrollProgress (useScroll / useWindowScroll), CopyButton (useClipboard), prop forwarding in primitives |
| nuxt | >=3.13 | optional: only for @nxgt/material-vue/nuxt |
The tarball ships dist/, plus lib/styles.css and lib/nuxt. There is no
postinstall build.
Setup
Styles
Import the stylesheet explicitly, in your Tailwind v4 entry, right after
@import "tailwindcss":
/* src/assets/main.css */
@import "tailwindcss";
@import "@nxgt/material-vue/styles.css";Nothing injects it for you, on purpose:
- Tailwind v4 builds one
@themefrom a single CSS graph.@theme/@utilityonly apply whenstyles.cssis imported in the same file as@import "tailwindcss"; a second stylesheet (for example another Nuxtcss[]entry) starts a second pipeline, and the tokens never reach the app. - Tailwind CSS IntelliSense reads the theme from that entry file. An explicit
import lets it autocomplete the library tokens (
bg-primary,text-error-foreground,rounded-lg, …) in your templates.
Tailwind v4 does not scan node_modules. styles.css already declares
@source "../dist", so your build generates the utilities used inside the
components. You don't need an extra @source.
| Import | What is in it |
| --- | --- |
| @import "@nxgt/material-vue/styles.css" | source Tailwind v4 tokens, @theme, @utility: this is what a Tailwind app imports |
| @import "@nxgt/material-vue" | style condition of the root export → dist/material-vue.css: compiled, self-contained CSS (tokens + every utility the components use), for apps without Tailwind |
Do not import both in the same app.
Pinia and router
Interactive widgets keep their UI state in Pinia stores created per instance, so Pinia must be installed before mount. The app shell and link buttons read the current route, so install vue-router as well:
// main.ts
import { createPinia } from 'pinia';
import { createApp } from 'vue';
import App from './App.vue';
import { router } from './router';
import './assets/main.css';
createApp(App).use(createPinia()).use(router).mount('#app');Icons
Icon renders a Font Awesome sprite from a root-relative URL on your
origin:
<script setup lang="ts">
import { Icon } from '@nxgt/material-vue/components';
</script>
<template>
<Icon name="atom" /> <!-- /assets/icons/sprites/duotone.svg#atom -->
<Icon name="gear" type="solid" /> <!-- /assets/icons/sprites/solid.svg#gear -->
<Icon name="gear" family="sharp" type="solid" /> <!-- /assets/icons/sprites/sharp-solid.svg#gear -->
</template>type defaults to duotone and family to classic. Serve the sprite
sheets from public/assets/icons/sprites/<file>.svg. This package does not
ship them, because the Font Awesome Pro licence does not cover redistribution
inside a library. A missing sheet renders nothing: no error, no fallback.
Some components draw an Icon themselves: the app shell menu, the mobile menu
button, LanguageSwitcher, LoadMoreButton and the toasts helpers. Other
glyphs come from Lucide and are bundled.
Nuxt
// nuxt.config.ts
import tailwindcss from '@tailwindcss/vite';
export default defineNuxtConfig({
modules: ['@nxgt/material-vue/nuxt'],
css: ['~/assets/css/main.css'], // imports styles.css, see above
vite: { plugins: [tailwindcss()] },
nxgtMaterial: {
// prefix: 'Nxgt', // <NxgtButton />
// components: true, // auto-import components
// imports: true, // auto-import composables and i18n helpers
},
});The module:
- Auto-imports components and composables
- Installs Pinia when the host app has none
- Pre-bundles CommonJS dependencies (
jsbarcode) for Vite
It does not add the stylesheet. Import it yourself as shown in Styles.
Subpaths
| Specifier | What is in it |
| --- | --- |
| @nxgt/material-vue | every barrel below, plus the compiled style condition (dist/material-vue.css, for apps without Tailwind) |
| @nxgt/material-vue/components | UI components and *FormField wrappers |
| @nxgt/material-vue/composables | useTheme, useIsClient, useIsMobile, useSearchParam, … |
| @nxgt/material-vue/dnd | drag-and-drop primitives |
| @nxgt/material-vue/i18n | I18nProvider, mergeResources, createTranslator, … |
| @nxgt/material-vue/lib | cn, clientOnly, date/number/string utils, Pinia store factories |
| @nxgt/material-vue/models | Status<T> |
| @nxgt/material-vue/types | shared function types |
| @nxgt/material-vue/styles.css | source Tailwind v4 tokens: this is what a Tailwind app imports |
| @nxgt/material-vue/nuxt | Nuxt module |
Usage
Components
<script setup lang="ts">
import { ref } from 'vue';
import { Button, Icon } from '@nxgt/material-vue/components';
const saving = ref(false);
</script>
<template>
<Button variant="filled" color="primary" :loading="saving" @click="saving = true">
<Icon name="plus" />
Add
</Button>
</template>Button and IconButton take variant (filled, tonal, outlined,
ghost, link) × color (primary, secondary, success, info,
warning, error, default), plus size, loading and tooltip.
Form fields
Every field is usable on its own with v-model. Most also have a vee-validate
wrapper in the same barrel (TextField → TextFormField): the wrapper takes
name instead of v-model and shows the validation error as helper text.
<script setup lang="ts">
import { useForm } from 'vee-validate';
import { Button, EmailFormField, TextFormField } from '@nxgt/material-vue/components';
const { handleSubmit } = useForm({ initialValues: { name: '', email: '' } });
const onSubmit = handleSubmit((values) => console.log(values));
</script>
<template>
<form @submit="onSubmit">
<TextFormField name="name" label="Name" required />
<EmailFormField name="email" label="Email" />
<Button type="submit">Save</Button>
</form>
</template>For translated Zod messages, register the error map once:
z.config({ customError: zodLocaleError() }) (from @nxgt/material-vue/lib).
i18n
This package owns the i18n mechanism, and your app owns the resource
content. Everything it ships lives under one namespace per language,
material. mergeResources deep-merges your bundle over it, so you can
override single strings (material.command.title). In development each
override is reported with console.warn.
// src/i18n.ts
import {
createTranslator,
createTypedTranslation,
type FlatObject,
mergeResources,
resources as materialResources,
} from '@nxgt/material-vue/i18n';
import { resources as appResources } from './resources';
export const resources = mergeResources(materialResources, appResources);
export type LocaleKey = keyof FlatObject<typeof resources.en, string>;
export const translate = createTranslator<LocaleKey>(resources);
export const useTranslation = createTypedTranslation<LocaleKey>(translate);<!-- App.vue: mount once, above everything that translates -->
<template>
<I18nProvider default-language="en">
<RouterView />
</I18nProvider>
</template>Inside any child component, const { t, i18n } = useTranslation() gives you
t plus i18n.language and i18n.changeLanguage. Only I18nProvider holds
the language. Without it, components fall back to their English copy.
Messages use ICU MessageFormat, and a missing key is returned verbatim.
App shell
The shell does not build or filter the menu: items is a prop, and every
label must already be translated. footer: true pins an entry to the bottom
of the sidebar. The shell holds no session, so the avatar comes from
avatar-src or the avatar slot.
<script setup lang="ts">
import {
ActivityLayout,
LanguageSwitcher,
type MenuItem,
ThemeToggle,
} from '@nxgt/material-vue/components';
const items: MenuItem[] = [
{ label: 'Home', url: '/', icon: 'house' },
{ label: 'Settings', url: '/settings', icon: 'gear', footer: true },
];
</script>
<template>
<ActivityLayout :items="items" avatar-src="/me.png">
<template #brand><img src="/logo.svg" alt="Acme" class="h-8" /></template>
<template #header-actions>
<LanguageSwitcher />
<ThemeToggle />
</template>
<RouterView />
</ActivityLayout>
</template>Always fill the brand slot, because the default one is a placeholder. The
active entry is the longest matching url across the whole menu, matched
segment by segment (/admin does not cover /administrators).
AppBreadcrumb lists every matched route that declares
meta: { breadcrumb: 'Settings' } (a string, or a function of the route).
Local stores
Widgets keep their UI state in Pinia stores that are local to each instance.
Use the same factory for your own components: every call from a component
setup() gets its own store id.
import { defineLocalStore } from '@nxgt/material-vue/lib';
export const useCounterStore = defineLocalStore<{ count: number }>('counter', {
state: () => ({ count: 0 }),
actions: {
increment(this: { count: number }) {
this.count += 1;
},
},
});
// in setup(): const store = useCounterStore({ count: 5 });Components
All components are exported from @nxgt/material-vue/components and from the
root barrel. The name before · is the standalone component, and the name
after it is the vee-validate wrapper. "+ parts" means the family also exports
sub-components (CardHeader, CardContent, …). The last column is the
matching export in @nxgt/material,
or — when the React library has none.
Primitives
Thin shadcn-vue / Reka UI building blocks, styled with the library tokens.
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| Accordion, AccordionItem, AccordionTrigger, AccordionContent | Collapsible sections, single or multiple open. | Accordion |
| Avatar, AvatarImage, AvatarFallback | Round image with a fallback (initials). | Avatar |
| Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter | Surface with header, body and footer areas. | Card |
| Checkbox | Checkbox with checked and indeterminate states. | Checkbox |
| Collapsible, CollapsibleTrigger, CollapsibleContent | Shows or hides one region. | Collapsible |
| Input | Styled native input with v-model. | Input |
| InputGroup, InputGroupAddon, InputGroupInput, InputGroupButton, InputGroupText, InputGroupTextarea | Input or textarea with inline addons, buttons and text. | InputGroup |
| InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator | Slot-by-slot one-time-code input. | — |
| Label | Form label; with-asterisk marks it required. | Label |
| NativeSelect, NativeSelectOption, NativeSelectOptGroup | Styled native <select> with v-model. | — |
| RadioGroup, RadioGroupItem | Radio primitives (see RadioGroupField for the labelled field). | RadioGroupBase, RadioGroupItem |
| ScrollArea, ScrollBar | Scroll container with styled scrollbars. | — |
| Select, SelectTrigger, SelectValue, SelectContent, SelectItem + parts | Select primitives (see SelectField for the labelled field). | Select |
| Separator | Horizontal or vertical rule. | Separator |
| Slider | Range slider primitive, one or more thumbs. | Slider |
| Switch | Switch; with label or placeholder it renders as a field row. | Switch |
| Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableEmpty + parts | Styled table markup. | Table |
| Tabs, TabsList, TabsTrigger, TabsContent | Tabbed panels. | Tabs |
| Textarea | Styled native textarea with v-model. | Textarea |
| Toggle, ToggleGroup, ToggleGroupItem | Pressable toggle and single/multiple toggle groups. | Toggle, ToggleGroup |
| ResizablePanelGroup, ResizablePanel, ResizableHandle | Split panes resized by dragging a handle. | ResizableGroup, ResizablePanel, ResizableSeparator |
| Typography | Text on the type scale (headline-large, title-medium, body-small, …). | Typography |
| AnchorHeading | Heading with a hover copy-link (always visible on the phone). | AnchorHeading |
| Kbd, KbdShortcut | Keyboard key; KbdShortcut renders ['mod', 'k'] as ⌘/Ctrl glyphs. | Kbd, KbdShortcut |
Buttons & actions
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| Button | Button with 5 variants × 7 colours, sizes, loading spinner and tooltip. | Button |
| IconButton | Round icon-only button with the same variants and colours. | IconButton |
| ButtonGroup | Lays out related buttons as one group. | ButtonGroup |
| ResponsiveButton | Label on desktop, icon only on mobile; expanded forces either. | ResponsiveButton |
| LinkButton, IconLinkButton, ResponsiveLinkButton | Buttons that navigate with vue-router (to, replace, target). | LinkButton, IconLinkButton, ResponsiveLinkButton |
| SplitButton | Main action plus a chevron menu filled by the menu slot. | SplitButton |
| CopyButton | Copies value to the clipboard and flips its tooltip to "Copied". | CopyButton |
| HighlightText | Marks case-insensitive matches of a query inside a string (splitMatch). | HighlightText |
| LoadMoreButton | Responsive "Load more" button with a refresh icon. | LoadMoreButton |
| ScrollToTop | Floating button shown past threshold px that scrolls back up. | ScrollToTop |
| ActionCard | Card with title, description and an active indicator. | ActionCard |
| Chip | Compact pill; clickable with @click, dismissible with @dismiss. | Chip |
Form fields
Every field takes label, helperText, error, required, disabled and
v-model.
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| TextField · TextFormField | Labelled text input. | TextField · TextFormField |
| EmailField · EmailFormField | Email input (autocomplete="email"). | EmailField · EmailFormField |
| PasswordField · PasswordFormField | Password with show/hide and an optional strength meter (show-strength). | PasswordField · PasswordFormField |
| UrlField · UrlFormField | URL input (autocomplete="url"). | UrlField · UrlFormField |
| PhoneField · PhoneFormField | Phone input with a country menu, parsed with libphonenumber-js. | PhoneField · PhoneFormField |
| NumberField · NumberFormField | Number input with min/max/step and stepper buttons. | NumberField · NumberFormField |
| CurrencyField · CurrencyFormField | Amount formatted for the current language; currency defaults to EUR. | CurrencyField · CurrencyFormField |
| PercentField · PercentFormField | Number field bounded to 0–100. | PercentField · PercentFormField |
| TextareaField · TextareaFormField | Textarea with min-rows/max-rows and an optional character count. | TextareaField · TextareaFormField |
| SearchField · SearchFormField | Search input with optional debounce (debounce-ms) and loading state. | SearchField · SearchFormField |
| ColorField · ColorFormField | Colour picker plus a hex text input. | ColorField · ColorFormField |
| OtpField · OtpFormField | One-time code boxes (length, default 6). | OtpField · OtpFormField |
| InputGroupField · InputGroupFormField | Labelled input with leading / trailing addon slots. | InputGroupField · InputGroupFormField |
| InlineEdit | Text that switches to an input and emits save. | InlineEdit · InlineEditFormField |
| RatingField · RatingFormField | Star rating (max, default 5), clearable. | RatingField · RatingFormField |
| SliderField · SliderFormField | Labelled slider, single value or range, with a readout. | Slider · SliderFormField |
| SwitchField · SwitchFormField | Labelled switch row bound to a boolean. | Switch · SwitchFormField |
| HelperText | Hint or error line under a field. | HelperText |
| Field | Renders any field from a config object ({ type: 'text' \| 'select' \| 'date' \| … }). | Field |
| Contacts, ContactItem, ContactDialog · ContactsFormField | Typed contact list (phone, email, …) with an add/edit dialog. | Contacts · ContactsFormField |
| PostalAddress, PostalAddressDialog · PostalAddressFormField | Shows a postal address and edits it in a dialog. | PostalAddress · PostalAddressFormField |
| OpeningHours, OpeningHoursItem, OpeningHoursDialog · OpeningHoursFormField | Weekly opening hours with a per-slot dialog. | OpeningHours · OpeningHoursFormField |
| AttributesField, AttributesList, AttributeDialog, AttributeForm · AttributesFormField | Defines custom typed attributes (text, number, select, date, …). | AttributesField · AttributesFormField |
| AttributeValuesForm, AttributeValueFormField · AttributeValuesFormFieldSet | Fills in values for a list of attribute definitions. | AttributeValuesForm · AttributeValuesFormFieldSet |
Date & time
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| Calendar + parts | Date grid with month and year navigation. | Calendar |
| DateField · DateFormField | Date picker in a popover, optional time (show-time-picker). | DateField · DateFormField |
| DateRangeField · DateRangeFormField | From/to range picker. | DateRangeField · DateRangeFormField |
| TimeField · TimeFormField | Time picker in a popover, bound to a Date. | TimeField · TimeFormField |
| TimePicker | Inline hour and minute inputs; emits change with a Date. | TimePicker |
| DurationField · DurationFormField | Hours + minutes, stored as total minutes. | DurationField · DurationFormField |
| TimezoneField · TimezoneFormField | Searchable IANA timezone picker. | TimezoneField · TimezoneFormField |
| RelativeTime | "3 minutes ago" for a date, in the current language. | RelativeTime |
| EventChip | Coloured event pill with time, all-day and continuation markers. | EventChip |
Selection & choice
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| SelectField · SelectFormField | Labelled select from options. | SelectField · SelectFormField |
| ComboboxField · ComboboxFormField | Searchable single select. | ComboboxField · ComboboxFormField |
| Autocomplete · AutocompleteFormField | Type-ahead single or multiple select; free-solo accepts new values. | Autocomplete · AutocompleteFormField |
| CountryField · CountryFormField | Country picker with flags, single or multiple. | CountryField · CountryFormField |
| CheckboxGroup · CheckboxGroupFormField | Checkbox list bound to string[]. | CheckboxGroup · CheckboxGroupFormField |
| RadioGroupField · RadioGroupFormField | Labelled radio list from options. | RadioGroup · RadioGroupFormField |
| SelectCardField · SelectCardFormField | Options as selectable cards (title, description), single or multiple. | SelectCardField · SelectCardFormField |
| CheckCardField · CheckCardFormField | A boolean shown as two cards (yes / no). | CheckCardField · CheckCardFormField |
| SelectChipField · SelectChipFormField | Options as chips, single or multiple. | SelectChipField · SelectChipFormField |
Data display
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| AccordionCard | Accordion built from an items array (title, content). | AccordionCard |
| AvatarGroup | Overlapping avatars; extras past max collapse into "+N". | AvatarGroup |
| Description | Label and value pair, hidden when the value is empty. | Description |
| SummaryData | Label/value rows, stacked or inline. | SummaryData |
| EntityHeader | Title with a row of metadata. | EntityHeader |
| StatCard | KPI tile: label, value, hint and a coloured delta. | StatCard |
| TrendCard, GoalCard, BreakdownCard, RankCard, CompareCard, HeatweekCard, FunnelCard, GaugeCard, SparkbarCard, DeltaListCard, BandCard, LatencyCard, RatioCard, ThresholdCard, PaceCard, HistogramCard | Dashboard tiles including compare, heat, funnel, gauge, spark bars, delta list, range band, latency, ratio, threshold, pace and labelled histogram. Container queries. | TrendCard, GoalCard, BreakdownCard, RankCard, CompareCard, HeatweekCard, FunnelCard, GaugeCard, SparkbarCard, DeltaListCard, BandCard, LatencyCard, RatioCard, ThresholdCard, PaceCard, HistogramCard |
| FeatureCard, MediaCard, PricingCard, ProfileCard, QuoteCard, OverlayCard, SpotlightCard, CrewCard, SplitCard, TicketCard, NoticeCard, TimelineCard, MessageCard, EventCard, StackCard, LinkCard, AlbumCard, TagCard, ReceiptCard, ReviewCard, OfferCard, PlaceCard, FileCard, StatusCard, ScoreCard, ShortcutCard, ChecklistCard | Card presets including overlay, ticket, inbox, event, stack, link, album, tags, receipt, review, offer, place, file, status, score, shortcuts and checklist. Container queries. | FeatureCard, MediaCard, PricingCard, ProfileCard, QuoteCard, OverlayCard, SpotlightCard, CrewCard, SplitCard, TicketCard, NoticeCard, TimelineCard, MessageCard, EventCard, StackCard, LinkCard, AlbumCard, TagCard, ReceiptCard, ReviewCard, OfferCard, PlaceCard, FileCard, StatusCard, ScoreCard, ShortcutCard, ChecklistCard |
| ExtendedLabel | Section heading with an accent bar and a trailing slot. | ExtendedLabel |
| Timeline, TimelineItem | Vertical event list with tones and timestamps. | Timeline, TimelineItem |
| QRCode | QR code for value (size, colours, error level). | QRCode |
| Barecode | Barcode drawn on a canvas with JsBarcode (CODE128 by default). | Barecode |
Data table, filter & lists
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| DataTable | TanStack table with sorting, filtering, row selection, expandable rows, pagination and load-more. | DataTable |
| DataTableColumnHeader, DataTablePagination, DataTableViewOptions | Sortable header, pager with page size, column visibility menu. | DataTableColumnHeader, DataTablePagination, DataTableViewOptions |
| Filter · FilterFormField | Filter panel built from a schema (15 field types), shown inline or in a dialog, sheet, drawer or popover. It supports presets, URL sync and storage. | Filter · FilterFormField |
| FilterProvider, FilterTrigger, FilterChips, FilterContent, FilterGroups, FilterGroup, FilterField, FilterActions, FilterPresets | Filter parts for composing your own layout. | FilterProvider, FilterChips, FilterContent, FilterGroups, FilterGroup, FilterField, FilterActions, FilterPresets |
| FilterInline, FilterDialog, FilterSheet, FilterDrawer, FilterPopover | Each filter container on its own. | FilterInline, FilterDialog, FilterSheet, FilterDrawer, FilterPopover |
| TransferList · TransferListFormField | Available/selected panes with search, move buttons and drag-and-drop. | TransferList · TransferListFormField |
| SortableList, SortableHandle · SortableListFormField | Searchable list you reorder by dragging; v-model is the ordered values. | SortableList, SortableHandle · SortableListFormField |
| Tree, TreeItem | Expandable tree with none, single or multiple selection. | Tree, TreeItem |
| ListTile | Row with title, subtitle and leading/trailing slots; a link or a button. size="sm" is the dense row. | ListTile |
| DndProvider, Draggable, Droppable, DraggableDroppable, SortableProvider, Sortable, DragHandle, DndOverlay | Drag-and-drop primitives with a dnd-kit-style API, also on /dnd. | DndProvider, Draggable, Droppable, DraggableDroppable, SortableProvider, Sortable, DragHandle, DndOverlay |
Navigation & layout
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| ActivityLayout, ActivityContent, AppSidebar, AppHeader, AppBreadcrumb | App shell: sidebar from items, header with a mobile menu sheet, route breadcrumb and page frame. | ActivityLayout, ActivityContent, AppSidebar, AppHeader, AppBreadcrumb |
| Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarMenuItem, SidebarMenuLabel | Collapsible navigation column (v-model:expanded). | Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarMenuItem, SidebarMenuLabel |
| NavigationRail | Narrow vertical rail for icon buttons. | NavigationRail |
| TopAppBar, BottomAppBar | Horizontal app bars; the bottom bar styles icon buttons with an active state. | TopAppBar, BottomAppBar |
| Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage + parts | Breadcrumb trail. | Breadcrumb |
| Pagination, PaginationContent, PaginationItem, PaginationFirst, PaginationLast + parts | Page navigation links. | Pagination, PaginationBar |
| Tabs | See Primitives. | Tabs |
| SkipLink | Skip-to-content control, visible on focus. DocSkipLink composes it with #doc-content. | SkipLink |
| Pager | Previous / next cards for a sequence of pages. DocPager composes it. | Pager |
| SeeAlso | Related-link list with outbound icons. DocSeeAlso composes it. | SeeAlso |
| Toc | On-this-page outline with a per-item active bar. Rail from xl, collapsible panel below. DocToc composes it. | Toc |
| Stepper | Horizontal or vertical steps with per-step status. | Stepper |
| Steps, StepsItem | Numbered editorial steps that stay visible on every breakpoint. | Steps, StepsItem |
| Hero | Landing band with eyebrow, title, supporting copy and actions. | Hero |
| Figure | Framed media with an optional caption. | Figure |
| Doc, DocPage, DocTopbar, DocSidebar, DocToc, DocPager, DocSearch, DocChildCards, DocSeeAlso, DocEmpty, DocProgress | Documentation site shell: sidebar from md, TOC from xl. Reading bar, copy-page / markdown, child cards, see also, and a 404 slot. Storybook UI/Doc/Api composes an API page from DocPage, AnchorHeading, ApiOperation, ParamTable and CodeGroup. | Doc, DocPage, DocTopbar, DocSidebar, DocToc, DocPager, DocSearch, DocChildCards, DocSeeAlso, DocEmpty, DocProgress |
| ListDetailsLayout (.List, .Details, .Trigger) | List and details panes with a switchable active view. | ListDetailsLayout |
| PaneLayout (.Pane, .Content, .Trigger) | Content with a toggleable side pane that becomes a sheet on mobile. | PaneLayout |
| ThreadLayout | Centred page frame with header and stepper slots. | ThreadLayout |
| ResponsiveGrid | Container-query grid, from 1 to 5 columns. | ResponsiveGrid |
| Background | Full-viewport gradient backdrop. | Background |
| Activity | Keeps its content mounted and toggles it with visible. | Activity |
| Switcher | Dropdown to switch the current item (workspace, account, …). | Switcher |
| ThemeToggle | Light / dark / system toggle backed by useTheme. | ThemeToggle |
| LanguageSwitcher | Language menu wired to I18nProvider; disabled without it. | LanguageSwitcher |
Overlays & menus
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| Dialog, DialogTrigger, DialogContent, DialogScrollContent + parts | Modal dialog. | Dialog |
| CustomDialog | Dialog with title and description props. | CustomDialog |
| AlertDialog, AlertDialogContent, AlertDialogAction, AlertDialogCancel + parts | Blocking modal that needs an explicit answer. | AlertDialog |
| CustomAlertDialog | Alert dialog with title and description props. | CustomAlertDialog |
| ConfirmDialog | Confirm/cancel modal with a destructive variant and loading. | ConfirmDialog |
| ConfirmationDialog | Confirm/cancel modal toned by variant and color (warning by default). | ConfirmationDialog |
| Sheet, SheetTrigger, SheetContent + parts | Panel sliding in from a screen edge. | Sheet |
| Drawer, DrawerTrigger, DrawerContent + parts | Drawer panel. | Drawer |
| Popover, PopoverTrigger, PopoverContent, PopoverAnchor | Anchored floating panel. | Popover |
| HoverCard, HoverCardTrigger, HoverCardContent | Card shown on hover. | HoverCard |
| Tooltip, TooltipProvider, TooltipTrigger, TooltipContent | Hint on hover or focus. | Tooltip |
| DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem + parts | Menu opened from a trigger, with checkbox, radio and sub-menus. | DropdownMenu |
| ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem + parts | Right-click menu. | ContextMenu |
| Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem + parts | Desktop-style menu bar. | Menubar |
| Command, CommandDialog, CommandInput, CommandList, CommandItem + parts | Command palette with search, groups and shortcuts. | Command, CommandDialog |
Feedback & status
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| Alert | Inline message with a title, description and tone. | Alert |
| Banner | Full-width notice (info, success, warning, error), optionally dismissible. | Banner |
| Toaster, Toast, toast, toasts | vue-sonner toasts; toasts.success / error / info / warning / loading / dismiss. | Toaster, Toast, toast, toasts |
| Progress, ProgressField, CircularProgress | Linear bar, labelled bar, and ring with a label. | Progress, ProgressField, CircularProgress |
| ScrollProgress | Hairline that tracks window or a scrollable target (@vueuse/core useWindowScroll / useScroll). DocProgress composes it. | ScrollProgress |
| Spinner | Loading spinner in several styles and colours. | Spinner |
| Skeleton, SkeletonText, SkeletonAvatar, SkeletonCard, SkeletonTable | Loading placeholders. | Skeleton, SkeletonText, SkeletonAvatar, SkeletonCard, SkeletonTable |
| EmptyState | Icon in a tinted well, title, description and action; not-found, unauthorized, generic, empty, offline, search and maintenance presets. | EmptyState |
| StatusIndicator | Status dot with a tone, optionally pulsing. | StatusIndicator |
| Badge | Pill label in the semantic tones. | Badge |
| CountBadge | Numeric badge capped at max ("99+"). | CountBadge |
| Notifications | Notification bell (popover or panel) with filters, categories, read and dismiss. | Notifications |
| HydrateLoading | Full-screen spinner over the gradient backdrop while the app boots. | HydrateLoading |
Media & files
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| UploadField · UploadFormField | File picker bound to File or File[]. | UploadField · UploadFormField |
| NetworkUploadField · NetworkUploadFormField | Drop zone that uploads through your onUpload(file, onProgress), with progress, retry and replace. | NetworkUploadField · NetworkUploadFormField |
| S3UploadField · S3UploadFormField | Same drop zone, uploading to presigned URLs from getPresignedUrl. | S3UploadField · S3UploadFormField |
| ImageField, ImageS3Field · ImageFormField, ImageS3FormField | Single image upload with preview. | ImageField, ImageS3Field · ImageFormField, ImageS3FormField |
| FileList, FileListField, FileListS3Field · FileListFormField, FileListS3FormField | File rows with size and remove; the field variants upload. | FileList, FileListField, FileListS3Field · FileListFormField, FileListS3FormField |
| ImageCropper, ImageCropperDialog | Crop, zoom and rotate an image, then emit the cropped File. | ImageCropper, ImageCropperDialog |
| Camera | Webcam preview, device choice and photo capture (client-only). | — |
| Gallery, LightboxGallery, MiniGallery | Image/video gallery (grid, masonry, justified, carousel, …) with lightbox and selection. | Gallery, LightboxGallery, MiniGallery |
| Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext | Embla carousel with previous/next buttons. | Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext |
| PdfReader, PdfToolbar, PdfSidebar, PdfPageList, PdfThumbnailSidebar, PdfOutlineSidebar | PDF viewer with zoom, fit, rotate, search, thumbnails and outline (client-only). PdfViewer is a deprecated alias. | PdfReader, PdfToolbar, PdfSidebar, PdfPageList, PdfThumbnailSidebar, PdfOutlineSidebar |
Rich widgets
| Component | What it does | @nxgt/material |
| --- | --- | --- |
| ChatInbox, ChatThread, ChatMessageList, ChatMessage, ChatComposer, ChatMarkdown | Chat kit: inbox (list + thread, container queries from @md), messages, markdown, retry and copy, and a composer. | ChatInbox, ChatThread, ChatMessageList, ChatMessage, ChatComposer, ChatMarkdown |
| ChatConversationItem, ChatMessageSeparator, ChatReactionGroup, ChatTypingIndicator, ChatComposerAttachments + parts | Conversation list row, date separator, reactions, typing dots and attachments. | ChatConversationItem, ChatMessageSeparator, ChatReactionGroup, ChatTypingIndicator, ChatComposerAttachments |
| Kanban | Board with columns, swimlane groups, WIP limits, drag-and-drop, search and quick add. | Kanban |
| EventCalendar | Month, week, work-week, day and agenda views; drag, resize and create events. | EventCalendar |
| BarChart, LineChart, AreaChart, PieChart, RadarChart, ScatterChart, HeatmapChart, FunnelChart, ChartContainer | ECharts wrappers themed from the CSS tokens, with loading and empty states. | BarChart, LineChart, AreaChart, PieChart, RadarChart, ScatterChart, HeatmapChart, FunnelChart, ChartContainer |
| EmojiPicker, EmojiList | Shared picker. ChatComposer emojis, Markdown :shortcode:, RTE toolbar. | EmojiPicker, EmojiList |
| MentionList | @ mention list from an items prop (local search). ChatComposer and the RTE suggestion list use it. | MentionList |
| RichTextEditor · RichTextEditorFormField | Tiptap editor: toolbar, tables, images, mentions, callouts, character limit. | RichTextEditor · RichTextEditorFormField |
| CodeEditor | Monaco editor with a language picker and word wrap (client-only). | CodeEditor · CodeEditorFormField |
| CodeBlock, CodeDiff, JsonViewer, CodeGroup | Highlighted code, side-by-side diff, collapsible JSON tree, and tabbed samples. | CodeBlock, CodeDiff, JsonViewer, CodeGroup |
| Markdown | GFM markdown with KaTeX math and mermaid diagrams (clientOnly). Fences reuse CodeBlock. ChatMarkdown renders through it. | Markdown |
| Terminal | Display-only log with prompt, copy, and error/input tones. Container queries. | Terminal |
| ApiEndpoint, ApiOperation | HTTP method badge, path and copy; ApiOperation adds a param table and example CodeGroup. | ApiEndpoint, ApiOperation |
| ParamTable | Name / type / default / description table; stacked cards below sm. | ParamTable |
| StxPlayer, StxPlayerProvider, StxPlayerViewport, StxPlayerControls, StxPlayerQueue, StxPlayerMini, StxPlayerExtras | Audio/video player with a queue, captions and a full or mini layout (client-only). VideoPlayer is a deprecated alias. | StxPlayer, StxPlayerProvider, StxPlayerViewport, StxPlayerControls, StxPlayerQueue, StxPlayerMini, StxPlayerExtras |
| Quiz, QuestionCard | Quiz and question cards composed from Card, ActionCard, GaugeCard, ChecklistCard, ProgressField and TextField. Container queries. | Quiz, QuestionCard |
| PrintHeader, PrintSection, PrintCard, PrintCardField, PrintInfoRow, PrintBadge | Inline-styled blocks for printable documents. | PrintHeader, PrintSection, PrintCard, PrintCardField, PrintInfoRow, PrintBadge |
| PrintTable, PrintTableHeader, PrintTableRow, PrintTableCell, PrintTotals, PrintTotalRow | Printable table with totals. | PrintTable, PrintTableHeader, PrintTableRow, PrintTableCell, PrintTotals, PrintTotalRow |
Helpers
Non-visual exports, by subpath. Signatures are shortened: ? marks an
optional argument.
@nxgt/material-vue/lib
| Export | What it does |
| --- | --- |
| cn(...classes) | Merges class names (clsx + tailwind-merge). |
| clientOnly(component) | Renders component only after mount, in the browser. Pair it with defineAsyncComponent(() => import(…)). |
| DATE_UTILS.format(iso, fmt?, locale?) | Formats an ISO string with date-fns ('PP' by default); returns - when empty. |
| DATE_UTILS.parseFromTimestring('HH:mm'), DATE_UTILS.parseFromISOString(iso?) | Parses a time string or an ISO string into a Date. |
| formatDateRangeFilter({ from, to }) | Converts a date range to ISO start/end of day, or undefined. |
| formatNumber(value, { language?, ...Intl.NumberFormatOptions }) | Intl.NumberFormat with the language per call; returns '' for NaN. |
| STRING_UTILS, enumToKebabCase(value?), joinTruthyParts(parts, separator?), splitMatch(text, query) | FOO_BAR → foo-bar; joins the non-empty parts; splits a string around case-insensitive matches. |
| STYLES_UTILS.colorFromVariable(name, alpha?), STYLES_UTILS.cssSize(value) | CSS variable to rgb(from var(--x) …); number to px. |
| safeGetItem(key, default), safeSetItem(key, value), safeRemoveItem(key), LOCALE_STORE_UTILS | localStorage access that never throws, with JSON for non-strings. |
| uniqueArray(array), findByKey(array, key, value), findByKeyIn(array, key, values) | De-duplicates; finds one or many items by a key. |
| useFindByKey(items, key), useFindById(items) | The same lookups bound to one list. |
| buildFieldOptions(values, { valueKey, label }) | Maps records to { value, label } options (a path or a function for label). |
| pick(obj, keys), omit(obj, keys), cleanObject(obj, cleanNulls?), cast<T>(value), castAsync<T>(promise) | Object helpers; cleanObject drops undefined (and null). |
| ifEmptyString(value, fallback), ifEmptyList(value, fallback) | Returns fallback for an empty string or list. |
| objectToFormData(obj), formDataToObject(formData), convertToFormData(files, fieldName?) | FormData conversions; files go under files by default. |
| searchParams(request), searchParam(request, key) | Reads query parameters from a Request (server handlers). |
| isExternalHref(href) | True for http(s): and mailto: — new tab, not in-app. |
| delay(ms) | Promise that resolves after ms. |
| renderSlot(slot, context), ContentSlot<T> | Resolves a prop that is either a VNode or a function of context. |
| zodLocaleError(), parsedType(data) | Zod v4 error map translated through material.zod.*: z.config({ customError: zodLocaleError() }). |
Pinia store factories. Every store is local to the component instance that creates it. The React library uses Redux Toolkit slices for the same jobs:
| Export | What it does | @nxgt/material |
| --- | --- | --- |
| defineLocalStore(name, { state, actions?, getters? }) | Returns a factory; call it in setup(), optionally with initial state, to get this instance's store. | useSliceReducer |
| localStoreId(name), resolveStoreState(base, overrides?) | Per-instance store id and the state merge behind defineLocalStore. | resolveSliceState |
| defineStatusStore(name, initial?, actions?) | status / data / error store with start, success, error, reset. | createStatusSlice |
| defineStatusesStore(name, initial, actions?) | The same, keyed by path (start('users'), statusOf('users')). | createStatusesSlice |
| runStatus(store, fn) | Runs an async function through a status store (start, then success or error). | createAsyncSlice |
| defineFilterStore(name, initial, actions?) | filter + sort store with updateFilter and resetFilter. | createSliceWithFilter |
| defineStepFormStore(name, initialValues?, actions?) | Multi-step form: step, values, isDirty, updateStep, updateFormValues, reset. | createStepFormSlice |
| createPinia, setActivePinia, storeToRefs | Re-exported from pinia. | — |
@nxgt/material-vue/composables
These are the React library's @nxgt/material/hooks, with the same names.
| Export | What it does |
| --- | --- |
| useTheme() | { theme, preference, updateTheme }: light/dark/system stored in localStorage.theme, toggles the dark class. |
| resolveTheme(stored, prefersDark), applyResolvedTheme(theme) | The pure logic behind useTheme, safe on the server. |
| useIsClient() | Ref<boolean> that turns true after mount; use it for hydration-safe branches. |
| useIsMobile() | Ref<boolean>, true below 768 px. |
| useHeadingSpy(ids, onActive, options?) | IntersectionObserver over heading ids (useIntersectionObserver); reports the topmost visible one. |
| useLocalizedDate() | { format, locale, language }: DATE_UTILS.format in the I18nProvider language. |
| useNumberFormatter() | { formatNumber, formatCurrency } in the current language. |
| useOptions(items, { value, label, filter?, expanded? }), buildOptions(items, config) | Maps records to { value, label } options, as a computed or a plain array. |
| extractNode(connection) | Unwraps a GraphQL edges[].node connection into an array. |
| useSearchParam(name) | Computed value of one query parameter (vue-router). |
| useNavigationHandler(to, { replace? }), useNavigationHandlers(routes) | Returns click handlers that push or replace a route. |
| useAllowedTransitions(current, transitions, configs) | Lists the workflow actions allowed from the current status. |
@nxgt/material-vue/i18n
| Export | What it does |
| --- | --- |
| I18nProvider, provideI18n(options) | Owns the language. Props: defaultLanguage, initialLanguage (for SSR), storageKey, supportedLanguages, onLanguageChange. |
| useI18nContext(), useOptionalI18nContext() | { language, supportedLanguages, changeLanguage }; the first throws without a provider, the second returns null. |
| mergeResources(base, app) | Deep, per-language merge of this package's bundle and yours; your leaf keys win. |
| createTranslator(resources), translate, getLanguage() | ICU translator over a bundle (translate is bound to the base bundle); missing keys are returned as-is. |
| createTypedTranslation(translate) | Builds your app's useTranslation(), which returns { t, i18n: { language, changeLanguage } }. |
| useMaterialTranslation() | Translator for this package's own material.* keys, with an English fallback. |
| resources, LANGUAGE_KEY, FALLBACK_LANGUAGE, SUPPORTED_LANGUAGES | Base bundle (en, fr), the storage key (language) and defaults. |
| FlatObject, Path, Language, MaterialLocaleKey, Translate, TranslationContext | Types for deriving your LocaleKey union. |
@nxgt/material-vue/models
| Export | What it does |
| --- | --- |
| Status<T> | { status: 'idle' \| 'loading' \| 'success' \| 'error'; data?; error? }, the shape the status stores use. |
@nxgt/material-vue/types
| Export | What it does |
| --- | --- |
| PromiseOr<T> | T \| Promise<T>. |
| FunctionOr<T, R>, AsyncFunctionOr<T, R> | A value, or a function (or async function) that computes it. |
| Predicate<T>, AsyncPredicate<T> | (arg: T) => boolean, sync or async. |
@nxgt/material-vue/dnd
The drag-and-drop layer used by kanban, transfer list, sortable list and calendar. The components are listed in the catalogue.
| Export | What it does |
| --- | --- |
| arrayMove(array, from, to), insertIds(list, ids, before?) | Moves one item; inserts ids into list before an id (or at the end). |
| closestCenter, pointerWithin, rectIntersection | Collision detection strategies. |
| verticalListSortingStrategy, horizontalListSortingStrategy | Sorting strategies for SortableProvider. |
| useDndItem(), useDndActiveId() | Context of the enclosing draggable item; id of the item being dragged. |
| CSS, composeRefs(...refs) | CSS.Transform.toString(transform) for inline styles; merges template refs. |
Component utilities (@nxgt/material-vue/components)
Pure helpers exported next to the components that use them.
| Export | What it does |
| --- | --- |
| formatFileSize(bytes), formatCount(count, max?), formatStatDelta(delta), formatRelativeTime(value, options?) | Display formatting for file sizes, counters (99+), KPI deltas and relative times. |
| isEmail(value), isUrl(value), normalizeUrl(value), normalizeHex(value), scorePassword(value) | Validation and normalisation behind the matching fields. |
| parseNumberInput(raw), clampNumber(value, min?, max?), stepNumber(value, direction, step?), parseCurrencyInput(raw) | Locale-tolerant number parsing (1.234,5 or 1,234.5) and stepping. |
| minutesToParts(total), partsToMinutes(hours, minutes), clampRating(value, max?) | Duration and rating conversions. |
| parsePhoneInput, toE164, formatInternational, formatNational, isValidPhoneNumber, PHONE_COUNTRIES, flagEmoji | Phone parsing and formatting (libphonenumber-js) and the country list. |
| TIMEZONES, timezoneLabel(tz, language?), timezoneCity(tz), toCalendarDate(date), fromDateValue(value, base?) | Timezone list and labels with UTC offset; Date ↔ calendar-date conversions. |
| copyToClipboard(text), debounce(fn, ms) | Clipboard write; debounced function. |
| findActiveMenuItem(path, items), partitionMenu(items) | The app shell's longest-match and footer rules. |
| dataTableFeatures, useColumnHelpers(), useDeleteRowsHandler(onDelete?) | TanStack feature set, column helpers and a confirm-then-delete handler for DataTable. |
| renderBarcode(canvas, value, options), renderQrModel(options), highlightCode(code, language?) | Barcode, QR and syntax highlighting without the component. |
| cropImageToFile(src, area, options?), IMAGE_CROP_ASPECTS | Crops an image into a File; preset aspect ratios. |
SSR
An SSR app that bundles this package (ssr.noExternal: ['@nxgt/material-vue'])
evaluates every module the barrels reach at boot, whether or not a page
renders the component.
Browser-only widgets are already deferred.
PdfReaderand its parts,Camera,StxPlayer,CodeEditorandCodeDiffload their dependency withimport()and render throughclientOnly. Do the same for your own browser-only code, because atypeof windowguard does nothing when the import itself is the side effect:import { defineAsyncComponent } from 'vue'; import { clientOnly } from '@nxgt/material-vue/lib'; export const MapView = clientOnly(defineAsyncComponent(() => import('./map-view.vue')));Language on first render. Pass
initial-language(from a cookie orAccept-Language) toI18nProviderso the server and the client agree.Never
<svg><title>. Some SSR frameworks hoist<title>into the document head, which causes a hydration mismatch. Userole="img"+aria-label;Iconalready does.
Traps
| Symptom | Cause | Fix |
| --- | --- | --- |
| Components render unstyled, no console error | styles.css is not in the same file as @import "tailwindcss" | import it right after @import "tailwindcss" in your entry CSS |
| Icon, menu icons or toast icons are blank | sprite sheet not served | public/assets/icons/sprites/<style>.svg |
| getActivePinia() was called but there was no active Pinia | Pinia not installed | app.use(createPinia()) before mount (the Nuxt module does it) |
| App shell, AppBreadcrumb or LinkButton throws on useRoute | vue-router not installed | app.use(router) |
| useI18nContext must be used within an I18nProvider | your useTranslation() runs outside the provider | mount I18nProvider above the component |
| ReferenceError: DOMMatrix at server boot | a static pdfjs-dist import in your own code | import() + clientOnly |
| jsbarcode has no default export (Vite, no Nuxt) | CommonJS dependency not pre-bundled | optimizeDeps.include: ['@nxgt/material-vue > jsbarcode'] |
| Both CSS files loaded, duplicated tokens | styles.css and the root style export imported together | keep one: styles.css with Tailwind, the compiled CSS without |
Development
bun install
bun run build # what consumers read
bun run storybook # http://localhost:6006
bun run testLicense
MIT. The LICENSE file ships in the
package.
