/Documentation

Snackbar

PublishedUpdated

A snackbar tells people about something the app has done, at the bottom of the screen: a message archived, a photo saved. It doesn't take focus or ask for anything, and has one action at most, such as Undo. For a decision, use a dialog. See the M3 snackbar guidelines.

Usage

show() puts it in a queue shared by every snackbar on the page: one shows at a time, the others wait their turn. Without an action it goes after 4 seconds.

Vanilla
import { createSnackbar } from 'mtrl';

const snackbar = createSnackbar({ message: 'Photo saved' });
document.body.append(snackbar.element);

function tell() {
  snackbar.show();
}
Web Components
<m-snackbar message="Photo saved"></m-snackbar>

<script type="module">
  const snackbar = document.querySelector('m-snackbar');

  function tell() {
    snackbar.show();
  }
</script>
React
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';

export function Example() {
  const [open, setOpen] = useState(false);
  const tell = () => setOpen(true);

  return (
    <Snackbar open={open} onClose={() => setOpen(false)} message="Photo saved" />
  );
}
Vue
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';

const open = ref(false);

function tell() {
  open.value = true;
}
</script>

<template>
  <MSnackbar :open="open" @close="open = false" message="Photo saved" />
</template>
Svelte
<script lang="ts">
  import { Snackbar } from 'mtrl/svelte';

  let open = $state(false);

  function tell() {
    open = true;
  }
</script>

<Snackbar open={open} onclose={() => (open = false)} message="Photo saved" />
SolidJS
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';

export function Example() {
  const [open, setOpen] = createSignal(false);
  const tell = () => setOpen(true);

  return (
    <Snackbar open={open()} onClose={() => setOpen(false)} message="Photo saved" />
  );
}

Examples

Undo

A snackbar with an action stays until it is acted on or dismissed, so there is time to reach it. The action closes it.

Vanilla
import { createSnackbar } from 'mtrl';

const snackbar = createSnackbar({ message: 'Message archived', action: 'Undo' });
snackbar.on('action', () => undoArchive());
document.body.append(snackbar.element);

function archive() {
  snackbar.show();
}
Web Components
<m-snackbar message="Message archived" action="Undo"></m-snackbar>

<script type="module">
  const snackbar = document.querySelector('m-snackbar');
  snackbar.addEventListener('action', () => undoArchive());

  function archive() {
    snackbar.show();
  }
</script>
React
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';
import { undoArchive } from './app';

export function Example() {
  const [open, setOpen] = useState(false);
  const archive = () => setOpen(true);

  return (
    <Snackbar onAction={() => undoArchive()} open={open} onClose={() => setOpen(false)} message="Message archived" action="Undo" />
  );
}
Vue
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';
import { undoArchive } from './app';

const open = ref(false);

function archive() {
  open.value = true;
}
</script>

<template>
  <MSnackbar @action="undoArchive()" :open="open" @close="open = false" message="Message archived" action="Undo" />
</template>
Svelte
<script lang="ts">
  import { Snackbar } from 'mtrl/svelte';
  import { undoArchive } from './app';

  let open = $state(false);

  function archive() {
    open = true;
  }
</script>

<Snackbar onaction={() => undoArchive()} open={open} onclose={() => (open = false)} message="Message archived" action="Undo" />
SolidJS
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';
import { undoArchive } from './app';

export function Example() {
  const [open, setOpen] = createSignal(false);
  const archive = () => setOpen(true);

  return (
    <Snackbar onAction={() => undoArchive()} open={open()} onClose={() => setOpen(false)} message="Message archived" action="Undo" />
  );
}

A close button, and how long it stays

dismissible adds a close button. duration is short (4s), long (10s), indefinite, or milliseconds.

Vanilla
import { createSnackbar } from 'mtrl';

const snackbar = createSnackbar({ message: 'Update available', dismissible: true, duration: 'long' });
document.body.append(snackbar.element);

function tell() {
  snackbar.show();
}
Web Components
<m-snackbar message="Update available" dismissible duration="long"></m-snackbar>

<script type="module">
  const snackbar = document.querySelector('m-snackbar');

  function tell() {
    snackbar.show();
  }
</script>
React
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';

export function Example() {
  const [open, setOpen] = useState(false);
  const tell = () => setOpen(true);

  return (
    <Snackbar open={open} onClose={() => setOpen(false)} message="Update available" dismissible duration="long" />
  );
}
Vue
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';

const open = ref(false);

function tell() {
  open.value = true;
}
</script>

<template>
  <MSnackbar :open="open" @close="open = false" message="Update available" dismissible duration="long" />
</template>
Svelte
<script lang="ts">
  import { Snackbar } from 'mtrl/svelte';

  let open = $state(false);

  function tell() {
    open = true;
  }
</script>

<Snackbar open={open} onclose={() => (open = false)} message="Update available" dismissible duration="long" />
SolidJS
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';

export function Example() {
  const [open, setOpen] = createSignal(false);
  const tell = () => setOpen(true);

  return (
    <Snackbar open={open()} onClose={() => setOpen(false)} message="Update available" dismissible duration="long" />
  );
}

Replacing the one on screen

When only the newest message matters, queueBehavior: 'replace' closes the snackbar on screen and drops the waiting ones.

Vanilla
import { createSnackbar } from 'mtrl';

const snackbar = createSnackbar({ message: 'Upload complete', queueBehavior: 'replace' });
document.body.append(snackbar.element);

function tell() {
  snackbar.show();
}
Web Components
<m-snackbar message="Upload complete" queue-behavior="replace"></m-snackbar>

<script type="module">
  const snackbar = document.querySelector('m-snackbar');

  function tell() {
    snackbar.show();
  }
</script>
React
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';

export function Example() {
  const [open, setOpen] = useState(false);
  const tell = () => setOpen(true);

  return (
    <Snackbar open={open} onClose={() => setOpen(false)} message="Upload complete" queueBehavior="replace" />
  );
}
Vue
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';

const open = ref(false);

function tell() {
  open.value = true;
}
</script>

<template>
  <MSnackbar :open="open" @close="open = false" message="Upload complete" queue-behavior="replace" />
</template>
Svelte
<script lang="ts">
  import { Snackbar } from 'mtrl/svelte';

  let open = $state(false);

  function tell() {
    open = true;
  }
</script>

<Snackbar open={open} onclose={() => (open = false)} message="Upload complete" queueBehavior="replace" />
SolidJS
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';

export function Example() {
  const [open, setOpen] = createSignal(false);
  const tell = () => setOpen(true);

  return (
    <Snackbar open={open()} onClose={() => setOpen(false)} message="Upload complete" queueBehavior="replace" />
  );
}

position places it at the start, center or end of the bottom edge. layer: 'top' shows it in the browser's top layer; while a modal dialog is open, it moves inside the dialog, so it can still be read and used. The web component always is in the top layer. clearSnackbars() closes the one on screen and drops the queue, for when what they were about goes away.

API

Options

Option Type Default Description
message string required The text, up to two lines
action string undefined The action's label
dismissible boolean false A close button
closeLabel string 'Dismiss' The close button's accessible name
duration 'short' | 'long' | 'indefinite' | number 'indefinite' with an action, else 'short' How long it stays: 4s, 10s, until closed, or milliseconds (0 is indefinite)
position 'center' | 'start' | 'end' 'center' Where along the bottom edge
queueBehavior 'queue' | 'replace' 'queue' Whether it waits its turn or replaces the queue
layer 'top' undefined Shows it in the top layer, inside the topmost modal dialog while one is open
onAction / onOpen / onClose (event: SnackbarEvent) => void undefined Handlers for action, open and close
on { open?, close?, action?, dismiss? } undefined Event handlers registered at creation
class string undefined Additional CSS classes
prefix string 'mtrl' Prefix for CSS class names

Methods

Method Parameters Returns Description
show() / hide() none SnackbarComponent Queues it to show, or closes it with reason api
getMessage() / setMessage(message) message: string string / SnackbarComponent The text
getAction() / setAction(text) text: string string / SnackbarComponent The action's label
getDuration() / setDuration(duration) duration: SnackbarDuration number / SnackbarComponent How long it stays, read in milliseconds; a change on screen starts the count again
getPosition() / setPosition(position) position: 'center' | 'start' | 'end' string / SnackbarComponent Where it sits
on(event, handler) / off(event, handler) event: string, handler: Function SnackbarComponent Adds or removes a listener
destroy() none void Removes it
Property Type Description
element HTMLElement The snackbar
state 'visible' | 'hidden' Whether it is on screen
actionButton / closeButton HTMLElement | undefined Its buttons

clearSnackbars(), from mtrl, closes the snackbar on screen and drops the waiting ones.

Events

Event Description Data
open It is on screen { snackbar, originalEvent }
action The action was used { snackbar, originalEvent }
close / dismiss It closed { snackbar, reason, originalEvent }

reason is timeout, action, close-button, escape, api or queue (replaced or cleared). The web component dispatches open, action and close; its close carries { reason }, and its open property is true from show() on, while it waits too.

Accessibility

  • A status live region: its message is announced politely, and focus is not moved to it.
  • Escape closes it while focus is inside it. If focus was inside when it closed, it goes back where it came from.
  • Its countdown pauses while the pointer is over it or focus is inside it. With an action, it stays until it is used or dismissed.
  • The close button is named by closeLabel.

Styling

Its colors are the inverse roles, so it reads as a surface of the opposite theme.

.mtrl-snackbar, .mtrl-snackbar--visible { }
.mtrl-snackbar--center, .mtrl-snackbar--start, .mtrl-snackbar--end { }
.mtrl-snackbar--with-action, .mtrl-snackbar--dismissible { }
.mtrl-snackbar--action-below { }   /* an action wider than 128dp, on its own line */
.mtrl-snackbar__text, .mtrl-snackbar__action, .mtrl-snackbar__close { }

Measurements

Attribute Value
Container inverse-surface, 4dp corners, elevation 3
Height 48dp, 68dp for two lines
Text Body Medium, inverse-on-surface, two lines at most
Action inverse-primary; below the text when wider than 128dp
Padding 16dp at the start, 8dp between the text and the action
Motion Fades on the fast effects spring, and scales from 0.8 on the fast spatial one