Skip to content

Snackbar Plugin ​

File Information ​

  • Path: app/plugins/snackbar.client.ts
  • Purpose: Global snackbar notification system
  • Type: Client-only (runs only in browser)
  • Provides: $snackbar (via provide: { snackbar })

Overview ​

The plugin renders notifications as plain DOM elements appended to document.body (not through a Vue component tree). Each call to show() builds a div.snackbar-plugin.snackbar-plugin-<color>, optionally mounts an icon component into it with its own small createApp(), slides it in, and removes it after the timeout. The older Snackbar.vue / useSnackbar state is kept only for type compatibility; useSnackbar() now delegates every call to this plugin.

Key Features ​

  1. Four colors - success, error, warning, info (SnackbarColor), each with a default icon component loaded lazily (SnackbarSuccess, SnackbarError, SnackbarWarning, SnackbarInfo from ~/components/svg/).
  2. Duplicate suppression (added in ee26b58) - show() returns early when a snackbar of the same color with exactly the same text is already on screen.
  3. Long default timeout - 300000 ms (5 minutes) when no timeout is passed to the plugin directly.
  4. Multiple snackbars - different messages, or the same text in a different color, still stack.

Code Structure ​

typescript
export default defineNuxtPlugin((nuxtApp) => {
  const snackbar: SnackbarMethods = {
    show(message, icon = null, color = 'success', timeout = 300000),
    hideSnackbar(snackbarEl),
    getBackgroundColor(color),
    success(message, icon?, timeout?),
    error(message, icon?, timeout?),
    warning(message, icon?, timeout?),
    info(message, icon?, timeout?),
  }

  return { provide: { snackbar } }
})

Methods ​

show(message, icon?, color?, timeout?) ​

ParamTypeDefault
messagestringrequired
iconComponent | nullnull (no icon)
colorSnackbarColor'success'
timeoutnumber (ms)300000

Order of work:

  1. Query .snackbar-plugin-<color> .snackbar-plugin-message; if any element's textContent === message, return without rendering.
  2. Create the wrapper and set --snackbar-color (from SNACKBAR_COLORS[color], falling back to success) and --snackbar-bg-color (from getBackgroundColor(color)).
  3. If icon is given, mount it with createApp(h(icon, { color })) and keep the app on the element (__iconApp) for cleanup.
  4. Append span.snackbar-plugin-message, append to document.body, add snackbar-plugin-visible on the next animation frame.
  5. If timeout > 0, call hideSnackbar() after timeout ms. A timeout of 0 keeps it on screen.

The dedupe landed in the same commit (ee26b58) that made the collage and folder detail pages show "Collage not found." / "Folder not found." from more than one code path, so the same message can be requested twice in quick succession.

success(message, icon?, timeout?) / error(...) / warning(...) / info(...) ​

Shortcuts for show(). icon defaults to the matching Snackbar* icon when undefined or null; timeout defaults to 300000. Note the second argument is the icon, not the timeout.

hideSnackbar(snackbarEl) ​

Removes snackbar-plugin-visible, then after 300 ms unmounts the icon app (if any) and removes the element.

getBackgroundColor(color) ​

Returns the background for a color, falling back to success.

Usage Examples ​

Success Notification ​

typescript
const { $snackbar } = useNuxtApp()
$snackbar.success('Operation completed successfully!')

Error Notification ​

typescript
const { $snackbar } = useNuxtApp()
$snackbar.error('Collage not found.')

Custom Timeout ​

typescript
const { $snackbar } = useNuxtApp()
// icon argument comes before timeout
$snackbar.info('Saved', undefined, 5000)

Using Composable ​

typescript
const snackbar = useSnackbar()
snackbar.showSuccess('Success!')
snackbar.showError('Folder not found.')

useSnackbar().addSnackbar() uses options.duration || options.timeout || 5000, so calls through the composable auto-hide after 5 s unless a duration is passed, while direct $snackbar.* calls default to 300000 ms.

Notification Colors ​

ColorText/border (SNACKBAR_COLORS)Background (getBackgroundColor)
success#256029#E5F6EB
error#B00020#FFEEF1
warning#856404#FFF2ED
info#0C5460#EEFFFD

Styling ​

From app/assets/scss/components/_snackbar.scss: fixed at top: 16px; right: 16px, max-width: 600px, min-height: 70px, border-radius: 8px, border widths 1px 1px 1px 3px in var(--snackbar-color), z-index: 9999.

Animation ​

Hidden state is transform: translateX(100%); opacity: 0; .snackbar-plugin-visible sets translateX(0) and opacity: 1. Removal waits 300 ms for the transition.

Auto-Dismiss ​

  • Plugin default: 300000 ms. Composable default: 5000 ms.
  • timeout = 0 disables auto-dismiss.

Close Button ​

The plugin does not render a close button; snackbars leave only through the timeout.

Dependencies ​

  • Vue - createApp, h, defineAsyncComponent for icons
  • ~/types - SnackbarColor, SnackbarMethods, SNACKBAR_COLORS

Notes for Development ​

  • Client-only plugin (.client.ts suffix); it touches document directly.
  • Dedupe compares exact textContent within the same color class only.
  • Stacked snackbars all use the same fixed position, so they overlap rather than list.