Skip to content

Message

A neo-brutalist functional message notification system (useMessage). Built with singleton container dynamic mounting on demand and TransitionGroup stacking animations — no component declaration needed in templates. For modal dialog containers, please refer to Dialog. For confirmation / prompt feedback dialogs, please refer to MessageBox.

Demo

Preview

四种消息类型

高级配置

Installation

pnpm dlx brutx-vue@latest add message

Usage

Basics

Use the info, success, warning, and error shortcut methods to display different types of messages.

vue
<script setup>
import { useMessage } from 'brutx-ui-vue'

const { info, success, warning, error } = useMessage()
</script>

<template>
    <button @click="info('Info', 'This is an informational message')">Info</button>
    <button @click="success('Success', 'Operation completed successfully')">Success</button>
    <button @click="warning('Warning', 'Please review your input')">Warning</button>
    <button @click="error('Error', 'Operation failed, please try again')">Error</button>
</template>

Custom Configuration

Use the show method with a full options object to customize duration, closability, and more.

vue
<script setup>
import { useMessage } from 'brutx-ui-vue'

const { show } = useMessage()

function showCustom() {
    show({
        type: 'warning',
        title: 'Session Expiring',
        description: 'Your session will expire in 5 minutes. Please save your work.',
        duration: 8000,
        closable: true,
    })
}

function showPersistent() {
    show({
        type: 'error',
        title: 'Network Disconnected',
        description: 'Please check your network settings and try again.',
        duration: 0,
    })
}
</script>

<template>
    <button @click="showCustom">Custom Duration (8s)</button>
    <button @click="showPersistent">No Auto-close</button>
</template>

Manual Close

Every method returns a close function. Call it to manually dismiss the corresponding message.

vue
<script setup>
import { useMessage } from 'brutx-ui-vue'
import { ref } from 'vue'

const { info } = useMessage()
const closeFn = ref(null)

function open() {
    closeFn.value = info('Loading', 'Processing your request, please wait...')
}

function close() {
    closeFn.value?.()
}
</script>

<template>
    <button @click="open">Open Message</button>
    <button @click="close">Close Manually</button>
</template>

Props

Message is invoked as a function (message.success(options) / useMessage() show(options)); the argument is MessageOptions. Key options:

PropTypeDefaultDescription
type'info' | 'success' | 'warning' | 'error''info'Message type
titlestring''Title text
descriptionstringDescription text
durationnumber3000Auto-close delay (ms); 0 disables auto-close
closablebooleantrueWhether to show the close button

Data Types

MessageType

ts
type MessageType = 'info' | 'success' | 'warning' | 'error'

MessageOptions

ts
interface MessageOptions {
    type?: 'info' | 'success' | 'warning' | 'error'
    title?: string
    description?: string
    duration?: number
    closable?: boolean
}
FieldTypeDefaultDescription
type'info' | 'success' | 'warning' | 'error''info'Message type
titlestring''Title text
descriptionstringDescription text
durationnumber3000Auto-close duration in milliseconds; set to 0 to disable auto-close
closablebooleantrueWhether to show the close button

MessageItem

ts
interface MessageItem {
    id: string
    type: MessageType
    title: string
    description?: string
    duration: number
    closable: boolean
}

ShowDialogOptions

ts
type RenderableContent = string | Component | VNode | (() => string | Component | VNode | null)

interface ShowDialogOptions {
    title?: string
    content?: RenderableContent
    footer?: RenderableContent
    draggable?: boolean
    dragHandle?: string | HTMLElement
    bounds?: 'parent' | 'viewport' | { top: number; left: number; right: number; bottom: number }
    initialPosition?: { x: number; y: number }
    resizable?: boolean
    minWidth?: number
    minHeight?: number
    maxWidth?: number
    maxHeight?: number
    aspectRatio?: number
    showCloseButton?: boolean
    forceMount?: boolean
    fullscreen?: boolean
    beforeClose?: ((done: () => void) => void) | (() => boolean | Promise<boolean>)
    destroyOnClose?: boolean
    zIndex?: number
    class?: string
}
FieldTypeDefaultDescription
titlestringDialog title
contentRenderableContentDialog content; supports string, component, VNode, or render function
footerRenderableContentFooter content
draggablebooleanfalseWhether the dialog is draggable
dragHandlestring | HTMLElementDrag handle (CSS selector or element)
bounds'parent' | 'viewport' | object'viewport'Drag boundaries
initialPosition{ x: number; y: number }Initial position
resizablebooleanfalseWhether the dialog is resizable
minWidthnumberMinimum width
minHeightnumberMinimum height
maxWidthnumberMaximum width
maxHeightnumberMaximum height
aspectRationumberLock aspect ratio
showCloseButtonbooleantrueWhether to show the close button
forceMountbooleanForce mount
fullscreenbooleanfalseFullscreen mode
beforeClose((done) => void) | (() => boolean | Promise<boolean>)Close hook (supports callback and Promise mode)
destroyOnClosebooleanfalseDestroy content after closing
zIndexnumberCustom z-index
classstringCustom CSS class

MessageBoxOptions

ts
interface MessageBoxOptions extends ShowDialogOptions {
    message?: string
    type?: 'info' | 'success' | 'warning' | 'error'
    showCancelButton?: boolean
    cancelButtonText?: string
    confirmButtonText?: string
    confirmButtonClass?: string
    cancelButtonClass?: string
    showInput?: boolean
    inputPlaceholder?: string
    inputValue?: string
    inputPattern?: RegExp
    inputErrorMessage?: string
}
FieldTypeDefaultDescription
messagestringPrompt message text
type'info' | 'success' | 'warning' | 'error'Message type
showCancelButtonbooleanfalseWhether to show the cancel button
cancelButtonTextstring'Cancel' / '取消'Cancel button text (auto-switches by locale)
confirmButtonTextstring'Confirm' / '确定'Confirm button text (auto-switches by locale)
confirmButtonClassstringCustom CSS class for the confirm button
cancelButtonClassstringCustom CSS class for the cancel button
showInputbooleanfalseWhether to show an input field
inputPlaceholderstringInput placeholder text
inputValuestring''Initial input value
inputPatternRegExpInput validation regex
inputErrorMessagestring'Invalid format'Validation error message

Composables

useMessage

ts
import { useMessage } from 'brutx-ui-vue'

const {
    info,      // (title: string, description?: string) => () => void
    success,   // (title: string, description?: string) => () => void
    warning,   // (title: string, description?: string) => () => void
    error,     // (title: string, description?: string) => () => void
    show,      // (options: MessageOptions) => () => void
} = useMessage()
MethodParametersReturnDescription
info(title: string, description?: string)() => voidShow an info message; returns a close function
success(title: string, description?: string)() => voidShow a success message; returns a close function
warning(title: string, description?: string)() => voidShow a warning message; returns a close function
error(title: string, description?: string)() => voidShow an error message; returns a close function
show(options: MessageOptions)() => voidShow a custom message; returns a close function

Key features:

  • Singleton pattern: Automatically mounts MessageContainer to body on first call — no manual declaration needed
  • Stacked animations: Multiple messages stack vertically via TransitionGroup with smooth reordering
  • Auto-close: Closes automatically after 3 seconds by default; customize with duration, set to 0 to disable
  • Manual close: Each method returns a close function — call it to dismiss the message immediately
  • Active message guard & Lazy GC: Equipped with dual component refCount and active message guards; active messages are never killed abruptly on unmount. Triggers a 3000ms lazy GC once all messages clear and refCount reaches 0
  • Explicit destruction: Exported destroyFallback() (or batch cleanup via destroyBrutxUI()) for test isolation and HMR resets
  • SSR safe: Uses an internal isClient guard; no side effects or errors in server-side rendering

useDialog

ts
import { useDialog } from 'brutx-ui-vue'

const {
    show,      // (options?: ShowDialogOptions) => { close: () => void; promise: Promise<void> }
} = useDialog()
MethodParametersReturnDescription
show(options?: ShowDialogOptions){ close, promise }Open a dialog

Return values:

PropertyTypeDescription
close() => voidManually close the dialog
promisePromise<void>Resolves when the dialog is closed

useMessageBox

ts
import { useMessageBox } from 'brutx-ui-vue'

const {
    show,      // (options?: MessageBoxOptions) => { close: () => void; promise: Promise<{ value: string } | undefined> }
} = useMessageBox()
MethodParametersReturnDescription
show(options?: MessageBoxOptions){ close, promise }Open a confirm/input prompt

Return values:

PropertyTypeDescription
close() => voidManually close the message box
promisePromise<MessageBoxResult>Always resolves { action, value? }: action='confirm' on confirm (value is the input when showInput); action='cancel' on cancel/close button/ESC/overlay; action='destroy' on manual destroy

Accessibility

  • Keyboard: Dialogs support Escape to close
  • ARIA Attributes: Messages use role="status" and aria-live="polite" to notify screen readers; the close button includes a localized aria-label for screen reader support
  • Focus Management: useDialog and useMessageBox are built on reka-ui's DialogRoot, trapping focus on open and restoring it on close
  • Reduced Motion: Respects the prefers-reduced-motion system setting and automatically simplifies transition animations

FAQ

Q: What is the difference between useMessage and useToast?

A: useMessage is a lightweight top-center notification, ideal for brief operation feedback. useToast offers richer features including configurable positions, stacking options, Promise tracking, and hover-to-pause — better suited for complex notification scenarios.

Q: Will message notifications cause errors in SSR?

A: No. useMessage uses an internal isClient guard, so calling it in a server-side rendering environment produces no side effects and no errors.

Q: How do I perform async validation before closing a dialog?

A: Use the beforeClose hook, which supports both callback and Promise modes:

vue
<script setup>
import { useDialog } from 'brutx-ui-vue'

const { show } = useDialog()

function openWithValidation() {
    show({
        title: 'Edit Profile',
        content: 'Form content...',
        beforeClose: async () => {
            const isValid = await validateForm()
            return isValid
        },
    })
}
</script>

Q: When does the useMessageBox promise reject?

A: It never rejects. The promise always resolves with MessageBoxResult: confirm button → { action: 'confirm' } (value included with showInput); cancel button / ESC / overlay / close button → { action: 'cancel' }; manual destroy(){ action: 'destroy' }. Check result.action === 'confirm' (the old .catch() branch is deprecated).

Brute force builds.