Skip to content

Toasts

Toast notifications are typically used to display messages to the user after an action has been performed. They are non-blocking notifications that can be dismissed by the user.

Basic usage

To display a toast, you can use one of the methods exposed by the useToasts composable.

vue
<script setup>
  import useToasts from '@leaflink/stash/useToasts';

  const toasts = useToasts();
</script>

<template>
  <Button @click="toasts.info('hey there 👋')">Show toast</Button>
</template>

Or with various severity levels:

template
<Button secondary color="blue" class="tw-mr-3" @click="toasts.success('Success toast')">Success</Button>
<Button secondary color="blue" class="tw-mr-3" @click="toasts.info('Info toast')">Info</Button>
<Button secondary color="red" class="tw-mr-3" @click="toasts.error('Error toast')">Error</Button>
<Button secondary color="red" @click="toasts.warning('Warning toast')">Warning</Button>

Installation

You don't have to do anything to start using toasts. Toasts are actually enabled as a separate plugin within Stash named ToastsPlugin. The plugin is installed automatically when using @leaflink/stash.

You should be able to call useToasts() right away after installing and running app.use('@leaflink/stash').

If you need to set toast options:

ts
import stash from '@leaflink/stash';

app.use(stash, {
  toasts: { 
    mountNodeId: 'some-id', 
    mountNodeClass: 'some-class', 
  }, 
});

If you don't need or want it to be loaded you can opt out:

ts
import stash from '@leaflink/stash';

app.use(stash, {
  toasts: false, 
});

If you opt out from loading it automatically from Stash, you can also load it manually:

ts
import { createApp } from 'vue';
import ToastsPlugin from '@leaflink/stash/ToastsPlugin';

const app = createApp(App);

app.use(ToastsPlugin); 

The ToastsPlugin can receive options as the second argument to app.use().

ts
import { createApp } from 'vue';
import ToastsPlugin from '@leaflink/stash/ToastsPlugin';

const app = createApp(App);

app.use(ToastsPlugin, { mountNodeClass: 'my-class' }); 

Manually

Alternatively, if you don't want to use ToastsPlugin, you can manually render the <Toasts /> component wherever you want in your application. Ideally, there should be only one instance of this component per app.

vue
<script setup lang="ts">
  import Toasts from '@leaflink/stash/Toasts.vue';
</script>

<template>
  <Toasts />
</template>

Advanced usage

To display a more complex structured content such as a rich text, you can pass down a render function or a template string as the content of the toast.

vue
<Button @click="toasts.success(() => h(MyToastContent))">
  Show toast
</Button>

See this section for more information on how to provide HTML content to the toast.

Testing

In order to ensure that toasts are being displayed during component tests, you need to use the stash plugin.

ts
import { render } from '@testing-library/vue';
import stash from '@leaflink/stash';
import userEvent from '@testing-library/user-event';

it('shows a toast when button is clicked', async () => {
  render(Component, {
    global: {
      plugins: [stash],
    },
  });

  const user = userEvent.setup();
  await user.click(screen.getByRole('button'));

  expect(await screen.findByText('I am a toast!')).toBeInTheDocument();
});

Cleaning up

Because toast notifications are displayed asynchronously and removed from the DOM after a timeout, you want to ensure that toasts are being cleared after every test. If you're using @leaflink/dom-testing-utils this is extremely easy.

In setup-env.ts, just add the following line:

ts
import '@leaflink/dom-testing-utils/setup-env';

If you're not using that library, you can also add this explicitly.

ts
import { config } from '@vue/test-utils';
import stash from '@leaflink/stash';
import useToasts from '@leaflink/stash/useToasts';

config.global.plugins = [stash];

afterEach(() => {
  useToasts().removeAll();
});

If you only need to do this for a single test for some reason, you can also do it in the test itself:

ts
import { render } from '@testing-library/vue';
import stash from '@leaflink/stash';
import useToasts from '@leaflink/stash/useToasts';
import userEvent from '@testing-library/user-event';

afterEach(() => {
  useToasts().removeAll();
});

it('shows a toast when button is clicked', async () => {
  render(Component, {
    global: {
      plugins: [stash],
    },
  });

  const user = userEvent.setup();
  await user.click(screen.getByRole('button'));

  expect(await screen.findByText('I am a toast!')).toBeInTheDocument();
});