@mezzanine-ui/vue
| Install | |
|---|---|
npm install @mezzanine-ui/vue |
|
| Latest Version: | 1.0.0-rc.7 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 27, 2026 |
| Links: | GitHub · npm |
Mezzanine UI
A comprehensive component library for React, Angular and Vue 3 with a complete design system, built for modern web applications.
📦 Version
Current releases:
{
"@mezzanine-ui/core": "1.2.4",
"@mezzanine-ui/react": "1.7.1",
"@mezzanine-ui/ng": "1.0.0-rc.18",
"@mezzanine-ui/vue": "1.0.0-rc.5",
"@mezzanine-ui/system": "1.0.2",
"@mezzanine-ui/icons": "1.0.2"
}
@mezzanine-ui/ngand@mezzanine-ui/vueare release candidates: the component set is complete and mirrors React prop-for-prop, and the public API may still receive minor adjustments before the stable release. Install them like any other package (see below).
📚 Documentation
- Storybook — unified hub that links the React, Angular and Vue Storybooks side by side
https://storybook.mezzanine-ui.org/react/— React canonical URLhttps://storybook.mezzanine-ui.org/angular/— Angular canonical URLhttps://storybook.mezzanine-ui.org/vue/— Vue canonical URL
- Migration Guide — Upgrading from previous versions
🌐 Browser Support
| Browser | Minimum Version |
|---|---|
| Google Chrome | 64 (2018) |
| Edge | 79 (2020) |
| Safari | 13.1 (2020) |
| Firefox | 69 (2019) |
⚡ Framework Support
Next.js (App Router & Pages Router)
Mezzanine UI fully supports Next.js including the App Router. All React components include the 'use client' directive, making them compatible with React Server Components architecture.
// app/layout.tsx - Works seamlessly with Next.js App Router
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<CalendarConfigProviderDayjs locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderDayjs>
</body>
</html>
);
}
All hooks and utilities are also SSR-safe and can be used in Next.js projects without additional configuration.
📦 Installation
React
yarn add @mezzanine-ui/core @mezzanine-ui/react @mezzanine-ui/system @mezzanine-ui/icons
Angular (release candidate)
Requires Angular 21+.
yarn add @mezzanine-ui/core @mezzanine-ui/system @mezzanine-ui/icons
yarn add @mezzanine-ui/ng
yarn add @angular/animations @angular/cdk @angular/common @angular/core
Vue 3 (release candidate)
Requires Vue 3.5+.
yarn add @mezzanine-ui/core @mezzanine-ui/system @mezzanine-ui/icons
yarn add @mezzanine-ui/vue
yarn add vue lodash
Date libraries (all frameworks)
If you plan to use date-related components (DatePicker, Calendar, TimePicker, etc.), install one of the supported date libraries:
# Choose one:
yarn add dayjs # Recommended - lightweight
yarn add moment # Legacy support
yarn add luxon # Alternative
🚀 Quick Start
1. Setup Styles
Create a main.scss file in your project:
@use '@mezzanine-ui/system' as mzn-system;
@use '@mezzanine-ui/core' as mzn-core;
// Apply design system variables
:root {
@include mzn-system.colors(light);
@include mzn-system.common-variables(default);
}
// Optional: Dark mode support
[data-theme='dark'] {
@include mzn-system.colors(dark);
}
// Optional: Compact mode support
[data-density='compact'] {
@include mzn-system.common-variables(compact);
}
// Import component styles
@include mzn-core.styles();
2. Import Styles
Import the stylesheet at your app's entry point:
import './main.scss';
function App() {
return <div>Your App</div>;
}
3. Use Components
import { Button, Typography } from '@mezzanine-ui/react';
import { PlusIcon } from '@mezzanine-ui/icons';
function App() {
return (
<div>
<Typography variant="h1">Welcome to Mezzanine UI</Typography>
<Button icon={PlusIcon} iconType="leading" size="main" variant="base-primary">
Click Me
</Button>
</div>
);
}
4. Setup CalendarConfigProvider (Required for Date Components)
If your application uses date-related components (DatePicker, DateRangePicker, Calendar, TimePicker, etc.), you must wrap your app with a CalendarConfigProvider. This provider configures the date library and locale settings.
Choose one of the following date libraries based on your project needs:
Using Day.js (Recommended - Lightweight)
yarn add dayjs
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';
function App({ children }) {
return <CalendarConfigProviderDayjs locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderDayjs>;
}
Using Moment.js
yarn add moment
import { CalendarConfigProviderMoment, CalendarLocale } from '@mezzanine-ui/react/moment';
function App({ children }) {
return <CalendarConfigProviderMoment locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderMoment>;
}
Using Luxon
yarn add luxon
import { CalendarConfigProviderLuxon, CalendarLocale } from '@mezzanine-ui/react/luxon';
function App({ children }) {
return <CalendarConfigProviderLuxon locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderLuxon>;
}
⚠️ Important: Import the provider from the specific entry point (e.g.,
@mezzanine-ui/react/dayjs) to avoid bundling unused date libraries.
📅 CalendarConfigProvider Configuration
Available Props
| Prop | Type | Default | Description |
|---|---|---|---|
locale |
CalendarLocaleValue |
CalendarLocale.EN_US |
Controls calendar display: first day of week, month/weekday names |
defaultDateFormat |
string |
'YYYY-MM-DD' |
Default format string for date values |
defaultTimeFormat |
string |
'HH:mm:ss' |
Default format string for time values |
Supported Locales
Common locale values (see CalendarLocale enum for full list):
| Locale | Value | First Day of Week |
|---|---|---|
CalendarLocale.EN_US |
'en-US' |
Sunday |
CalendarLocale.EN_GB |
'en-GB' |
Monday |
CalendarLocale.ZH_TW |
'zh-TW' |
Sunday |
CalendarLocale.ZH_CN |
'zh-CN' |
Monday |
CalendarLocale.JA_JP |
'ja-JP' |
Sunday |
CalendarLocale.KO_KR |
'ko-KR' |
Sunday |
CalendarLocale.DE_DE |
'de-DE' |
Monday |
CalendarLocale.FR_FR |
'fr-FR' |
Monday |
Usage with Next.js App Router
For Next.js App Router, place the provider in your root layout:
// app/layout.tsx
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<CalendarConfigProviderDayjs locale={CalendarLocale.ZH_TW}>{children}</CalendarConfigProviderDayjs>
</body>
</html>
);
}
Custom Date/Time Formats
import { CalendarConfigProviderDayjs, CalendarLocale } from '@mezzanine-ui/react/dayjs';
function App({ children }) {
return (
<CalendarConfigProviderDayjs defaultDateFormat="YYYY/MM/DD" defaultTimeFormat="HH:mm" locale={CalendarLocale.ZH_TW}>
{children}
</CalendarConfigProviderDayjs>
);
}
Date Library Comparison
| Library | Bundle Size | Tree-Shakeable | ISO Week Support | Note |
|---|---|---|---|---|
| Day.js | ~2KB | Yes | Yes | Recommended for most projects |
| Moment | ~70KB | No | Yes | Legacy support |
| Luxon | ~20KB | Yes | Yes (ISO only) | Always uses Monday as first day |
🅰️ Angular Quick Start
The Angular package mirrors the React API prop-for-prop. Components are distributed as secondary entry points — import each component from its own path so that Angular's tree-shaker only pulls in what you use.
1. Share the same SCSS setup
@mezzanine-ui/ng consumes the same @mezzanine-ui/core + @mezzanine-ui/system tokens as React. Reuse the main.scss shown in the React Quick Start verbatim and load it from your Angular app's styles array (angular.json) or global stylesheet.
2. Import components from secondary entry points
// app.component.ts
import { Component } from '@angular/core';
import { MznButton } from '@mezzanine-ui/ng/button';
import { MznTypography } from '@mezzanine-ui/ng/typography';
import { MznIcon } from '@mezzanine-ui/ng/icon';
import { PlusIcon } from '@mezzanine-ui/icons';
@Component({
selector: 'app-root',
standalone: true,
imports: [MznButton, MznTypography, MznIcon],
template: `
<mzn-typography variant="h1">Welcome to Mezzanine UI</mzn-typography>
<mzn-button variant="base-primary" size="main" [icon]="plusIcon" iconType="leading"> Click Me </mzn-button>
`,
})
export class AppComponent {
plusIcon = PlusIcon;
}
⚠️ Do not barrel-import from
@mezzanine-ui/ng. Always use the per-component path (e.g.@mezzanine-ui/ng/button) so ng-packagr's secondary entry points can be code-split correctly.
3. API parity with React
Angular inputs/outputs mirror React props 1:1, including both flat top-level props (e.g. disabledMonthSwitch, placeholderLeft) and bundle props (e.g. calendarProps, popperProps). When both are provided, flat props win. See .claude/skills/architecting-angular-components/SKILL.md for the full contract.
💚 Vue Quick Start
The Vue package mirrors the React API prop-for-prop. Every component is prefixed with Mzn and distributed as a sub-path entry point — import each component from its own path so that your bundler only pulls in what you use.
1. Share the same SCSS setup
@mezzanine-ui/vue ships no stylesheet of its own; all styles come from @mezzanine-ui/core + @mezzanine-ui/system. Reuse the main.scss shown in the React Quick Start verbatim and import it once in your app entry:
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import './main.scss';
createApp(App).mount('#app');
2. Import components from sub-path entry points
<!-- App.vue -->
<script setup lang="ts">
import { MznButton } from '@mezzanine-ui/vue/button';
import { MznTypography } from '@mezzanine-ui/vue/typography';
import { PlusIcon } from '@mezzanine-ui/icons';
</script>
<template>
<MznTypography variant="h1">Welcome to Mezzanine UI</MznTypography>
<MznButton :icon="PlusIcon" icon-type="leading" size="main" variant="base-primary">Click Me</MznButton>
</template>
⚠️ The package root
@mezzanine-ui/vueexports no components. Always use the per-component path (e.g.@mezzanine-ui/vue/button). Seepackages/vue/COMPONENTS.mdfor the full list of import names and paths.
3. Setup CalendarConfigProvider (Required for Date Components)
Date-related components (DatePicker, DateRangePicker, Calendar, TimePicker, etc.) read their date library and locale from a MznCalendarConfigProvider. Wrap your app once with the preset provider for the date library you installed — all presets live in @mezzanine-ui/vue/calendar:
| Preset | Date library |
|---|---|
MznCalendarConfigProviderDayjs |
Day.js |
MznCalendarConfigProviderMoment |
Moment.js |
MznCalendarConfigProviderLuxon |
Luxon |
<!-- App.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import { CalendarLocale, MznCalendarConfigProviderDayjs } from '@mezzanine-ui/vue/calendar';
import { MznDatePicker } from '@mezzanine-ui/vue/date-picker';
const date = ref<string>();
</script>
<template>
<MznCalendarConfigProviderDayjs :locale="CalendarLocale.ZH_TW">
<MznDatePicker :value="date" placeholder="Select a date" @change="(value) => (date = value)" />
</MznCalendarConfigProviderDayjs>
</template>
The preset providers accept the same locale, defaultDateFormat and defaultTimeFormat props documented in CalendarConfigProvider Configuration.
🎨 Design System Customization
Understanding Primitives vs Semantic
Mezzanine UI v2 uses a two-layer design token system:
- Primitives: Raw values (e.g.,
#3b82f6,16px) - Semantic: Contextual tokens that reference primitives (e.g.,
text-brand,padding-base)
💡 Best Practice: Always use semantic tokens in your application for automatic theme switching support.
Customize Color Palette
Override palette colors by passing a custom configuration:
@use '@mezzanine-ui/system' as mzn-system;
$custom-palette: (
background: (
base: (
light: #000,
dark: #fff,
),
menu: (
light: #fff,
dark: #9a9a9a,
),
// ...
), // ...
);
:root {
@include mzn-system.colors(light, $custom-palette);
}
[data-theme='dark'] {
@include mzn-system.colors(dark, $custom-palette);
}
Customize Typography
Override typography settings:
@use '@mezzanine-ui/system' as mzn-system;
@use '@mezzanine-ui/system/typography' as typography;
$custom-variables: (
typography: (
h3: (
font-size: 18px,
font-weight: 700,
line-height: 26px,
letter-spacing: 0,
),
// ...
),
);
:root {
@include mzn-system.common-variables(default, $custom-variables);
}
Customize Spacing
Override spacing values:
@use '@mezzanine-ui/system' as mzn-system;
$custom-variables: (
spacing: (
size: (
element: (
hairline: (
default: 2px,
compact: 2px,
),
),
),
),
);
:root {
@include mzn-system.common-variables(default, $custom-variables);
}
[data-density='compact'] {
@include mzn-system.common-variables(compact, $custom-variables);
}
🎯 Using Design Tokens in Your Components
In SCSS
@use '@mezzanine-ui/system/palette' as palette;
@use '@mezzanine-ui/system/spacing' as spacing;
@use '@mezzanine-ui/system/radius' as radius;
@use '@mezzanine-ui/system/typography' as typography;
.my-component {
// Colors - use semantic variables
color: palette.semantic-variable(text, brand);
background-color: palette.semantic-variable(background, base);
border-color: palette.semantic-variable(border, neutral);
// Spacing - use semantic variables
padding: spacing.semantic-variable(padding, horizontal, base);
gap: spacing.semantic-variable(gap, tight);
// Border radius
border-radius: radius.variable(base);
// Typography - apply full semantic typography
@include typography.semantic-variable(body);
}
Available System Modules
| Module | Purpose | Example |
|---|---|---|
palette |
Colors (text, background, etc.) | palette.semantic-variable(text, brand) |
spacing |
Padding, margin, gap | spacing.semantic-variable(padding, base) |
radius |
Border radius | radius.variable(base) |
typography |
Font settings | @include typography.semantic-variable(button) |
effect |
Shadows, focus rings | effect.variable(focus, primary) |
size |
Element sizes | size.semantic-variable(element, main) |
🔧 Using React Components
Component Examples
Button
import { Button } from '@mezzanine-ui/react';
import { PlusIcon } from '@mezzanine-ui/icons';
<Button variant="base-primary" size="main">
Primary Button
</Button>
<Button variant="base-secondary" size="sub" disabled>
Disabled Button
</Button>
<Button icon={PlusIcon} iconType="leading" size="minor" variant="outlined-primary">
With Icon
</Button>
Typography
import { Typography } from '@mezzanine-ui/react';
<Typography variant="h1">Heading 1</Typography>
<Typography variant="body">Body text</Typography>
<Typography variant="caption" color="text-neutral">
Caption text
</Typography>
DatePicker
Requires
CalendarConfigProviderwrapper (see Setup CalendarConfigProvider)
import { useState } from 'react';
import { DatePicker } from '@mezzanine-ui/react';
function Example() {
const [date, setDate] = useState<string>();
return (
<DatePicker
onChange={setDate}
placeholder="Select a date"
value={date}
/>
);
}
DateRangePicker
import { useState } from 'react';
import { DateRangePicker } from '@mezzanine-ui/react';
function Example() {
const [range, setRange] = useState<[string, string]>();
return (
<DateRangePicker
onChange={setRange}
value={range}
/>
);
}
Layout
Layout is the top-level page shell for building full-application frames. It manages a responsive structure with a sticky navigation sidebar and optional resizable side panels. The sub-components (Layout.Main, Layout.LeftPanel, Layout.RightPanel) can be placed in any order — the layout always renders them in the correct visual sequence.
| Sub-component | Purpose |
|---|---|
<Navigation> |
Sticky left navigation sidebar |
<Layout.Main> |
Main scrollable content area (required) |
<Layout.LeftPanel> |
Resizable left panel (optional) |
<Layout.RightPanel> |
Resizable right panel (optional) |
Basic layout with navigation:
import { useState } from 'react';
import { Layout, Navigation, NavigationFooter, NavigationHeader, NavigationOption } from '@mezzanine-ui/react';
import { FileIcon, HomeIcon } from '@mezzanine-ui/icons';
function App() {
const [activatedPath, setActivatedPath] = useState(['Home']);
return (
<Layout>
<Navigation activatedPath={activatedPath} onOptionClick={(path) => path && setActivatedPath(path)}>
<NavigationHeader title="My App" />
<NavigationOption icon={HomeIcon} title="Home" />
<NavigationOption icon={FileIcon} title="Reports">
<NavigationOption title="Traffic" />
<NavigationOption title="Conversion" />
</NavigationOption>
<NavigationFooter />
</Navigation>
<Layout.Main>
<h1>Page Content</h1>
</Layout.Main>
</Layout>
);
}
With a toggleable right panel:
import { useState } from 'react';
import { Layout, Navigation, NavigationFooter, NavigationHeader } from '@mezzanine-ui/react';
function App() {
const [rightOpen, setRightOpen] = useState(false);
return (
<Layout>
<Navigation>
<NavigationHeader title="My App" />
<NavigationFooter />
</Navigation>
<Layout.Main>
<button onClick={() => setRightOpen(true)}>Open Details</button>
</Layout.Main>
<Layout.RightPanel defaultWidth={320} open={rightOpen}>
<div>
<h2>Details</h2>
<button onClick={() => setRightOpen(false)}>Close</button>
</div>
</Layout.RightPanel>
</Layout>
);
}
With dual panels (left + right):
import { useState } from 'react';
import { Layout, Navigation, NavigationFooter, NavigationHeader } from '@mezzanine-ui/react';
function App() {
const [leftOpen, setLeftOpen] = useState(true);
const [rightOpen, setRightOpen] = useState(false);
return (
<Layout>
<Navigation>
<NavigationHeader title="My App" />
<NavigationFooter />
</Navigation>
<Layout.LeftPanel defaultWidth={240} open={leftOpen}>
<div>Sidebar filters, navigation trees, etc.</div>
</Layout.LeftPanel>
<Layout.Main>
<h1>Main Content</h1>
{!rightOpen && <button onClick={() => setRightOpen(true)}>Open Right</button>}
</Layout.Main>
<Layout.RightPanel defaultWidth={320} open={rightOpen}>
<div>Detail view, preview, contextual actions, etc.</div>
</Layout.RightPanel>
</Layout>
);
}
Layout.LeftPanel / Layout.RightPanel props:
| Prop | Type | Default | Description |
|---|---|---|---|
open |
boolean |
false |
Controls panel visibility |
defaultWidth |
number |
320 |
Initial width in px (minimum 240) |
onWidthChange |
(width: number) => void |
— | Callback when the panel is resized |
scrollbarProps |
ScrollbarProps |
— | Props forwarded to the inner scrollbar |
Panels are resizable by dragging the divider. Focus the divider and use
←/→arrow keys to resize with keyboard.
🪝 React Hooks
Mezzanine provides several utility hooks for common UI patterns:
| Hook | Description |
|---|---|
useClickAway |
Detect clicks outside an element (useful for dropdowns, modals) |
useComposeRefs |
Compose multiple refs into one |
useDocumentEscapeKeyDown |
Listen for ESC key press on document |
useDocumentTabKeyDown |
Listen for Tab key press on document |
useDocumentEvents |
Generic document event listener with cleanup |
useIsomorphicLayoutEffect |
SSR-safe useLayoutEffect (uses useEffect on server) |
useLastCallback |
Stable callback reference that always calls the latest version |
useLastValue |
Ref that always holds the latest value |
usePreviousValue |
Access the previous render's value |
useScrollLock |
Lock body scroll (for modals/overlays) with scrollbar gap compensation |
useTopStack |
Manage stacking order for overlays |
useWindowWidth |
Track window width with resize listener |
Example: useClickAway
import { useRef } from 'react';
import { useClickAway } from '@mezzanine-ui/react';
function Dropdown({ onClose }) {
const dropdownRef = useRef();
useClickAway(() => (event) => onClose(event), dropdownRef, [onClose]);
return <div ref={dropdownRef}>Dropdown content</div>;
}
Example: useScrollLock
import { useScrollLock } from '@mezzanine-ui/react';
function Modal({ isOpen }) {
// Automatically locks scroll when modal is open
useScrollLock({ enabled: isOpen });
return isOpen ? <div className="modal">Modal content</div> : null;
}
🛠️ Utility Functions
Mezzanine also exports commonly used utility functions:
| Utility | Description |
|---|---|
formatNumberWithCommas |
Format numbers with locale-aware thousands separators |
parseNumberWithCommas |
Parse comma-formatted string back to number |
getCSSVariableValue |
Get computed CSS variable value from :root |
getNumericCSSVariablePixelValue |
Get CSS variable as numeric pixel value |
arrayMove |
Move array item from one index to another (immutable) |
cx |
Classname utility (re-exported from clsx) |
composeRefs |
Compose multiple refs (non-hook version) |
getScrollbarWidth |
Get current scrollbar width in pixels |
Example: Number Formatting
import { formatNumberWithCommas, parseNumberWithCommas } from '@mezzanine-ui/react';
// Format number for display
formatNumberWithCommas(1234567); // '1,234,567'
formatNumberWithCommas(1234567, 'de-DE'); // '1.234.567'
// Parse back to number
parseNumberWithCommas('1,234,567'); // 1234567
Example: CSS Variables
import { getCSSVariableValue } from '@mezzanine-ui/react';
// Read design token values at runtime
const brandColor = getCSSVariableValue('--mzn-color-text-brand');
🌙 Theme Support
Light/Dark Mode
Mezzanine UI v2 has built-in support for light and dark modes:
// In your SCSS
:root {
@include mzn-system.colors(light);
}
[data-theme='dark'] {
@include mzn-system.colors(dark);
}
Toggle theme in your React app:
function ThemeToggle() {
const [theme, setTheme] = useState('light');
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
}, [theme]);
return <Button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>Toggle Theme</Button>;
}
Default/Compact Mode
Switch between comfortable and compact spacing:
:root {
@include mzn-system.common-variables(default);
}
[data-density='compact'] {
@include mzn-system.common-variables(compact);
}
Toggle density in your React app:
function DensityToggle() {
const [density, setDensity] = useState('default');
useEffect(() => {
document.documentElement.setAttribute('data-density', density);
}, [density]);
return <Button onClick={() => setDensity(density === 'default' ? 'compact' : 'default')}>Toggle Density</Button>;
}
📖 Icon Usage
Use icons from the @mezzanine-ui/icons package:
import Icon from '@mezzanine-ui/react/Icon';
import { CheckIcon, ChevronDownIcon, PlusIcon } from '@mezzanine-ui/icons';
function Example() {
return (
<div>
<Icon icon={ChevronDownIcon} />
<Icon icon={PlusIcon} />
<Icon icon={CheckIcon} />
</div>
);
}
🤝 Contributing
We welcome contributions! Please see our Development Guidelines for:
- Setting up the development environment
- Understanding the project architecture
- Following coding conventions
- Writing tests and documentation
📝 License
MIT License - see LICENSE for details.
🔗 Links
Version History
Pre-releases (8)
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.0-rc.7 | >=3.5 | MIT | |
| 1.0.0-rc.6 | >=3.5 | MIT | |
| 1.0.0-rc.5 | >=3.5 | MIT | |
| 1.0.0-rc.4 | >=3.5 | MIT | |
| 1.0.0-rc.3 | >=3.5 | MIT | |
| 1.0.0-rc.2 | >=3.5 | MIT | |
| 1.0.0-rc.1 | >=3.5 | MIT | |
| 1.0.0-rc.0 | >=3.5 | MIT |