Toast

A succinct message that is displayed temporarily.

<ToastTrigger variant="outline" toast_target="toast-default">Show default Toast</ToastTrigger>

<Toast id="toast-default" duration="4000">
  <ToastContent
      type="default"
      title="Hello"
      description="Your toast is ready."
  />
</Toast>

Installation

uvx --from basic-components components add toast
pipx run --spec basic-components components add toast
pip install basic-components && components add toast
  • Copy Toast components below to your local environment
  • Place them in the components/ui/toast directory in your project
  • Configure your jinja environment for JinjaX
  • Add the cn() helper function to your global jinja environment

Usage

<ToastTrigger variant="outline" toast_target="toast-default">Show default Toast</ToastTrigger>

<Toast id="toast-default" duration="4000">
  <ToastContent
      type="default"
      title="Hello"
      description="Your toast is ready."
  />
</Toast>

Attributes

Component Prop Type Default Description
Toast id String The css id for the Toast.
Toast duration Number 30000 Duration in milliseconds before toast auto-dismisses.
ToastContent type String "default" Style variant of the toast. Options: "default", "success", "error", "warning", "destructive".
ToastContent title String "" The title text displayed in the toast.
ToastContent description String "" The description text displayed below the title.
ToastTrigger variant String "default" The Button component variant to use.
ToastTrigger toast_target String The target toast identifier to trigger.

Toast components require an id so that the dispatch event can call the component to display it.

Code

{# def
    id: str,
    className: str = "",
    duration: int = 30000,  # duration in milliseconds
#}
<div id="toast"
    x-data="{
    show: false,
    timeoutId: null,
    resetTimeout() {
      clearTimeout(this.timeoutId);
      this.timeoutId = setTimeout(() => { this.show = false }, {{ duration }});
    },
    handleShow() {
      console.log('this is handleShow');

      // If already showing, briefly hide and show again to restart animation
      if (this.show) {
        this.show = false;
        setTimeout(() => {
          this.show = true;
          this.resetTimeout();
        }, 100);
      } else {
        this.show = true;
        //this.resetTimeout();
      }
    }
  }"
    @show-toast.window="console.log('TOAST'); console.log($event.detail); console.log('{{ id }}');  if($event.detail === '{{ id }}' || $event.detail.value === '{{ id }}') { handleShow(); } else { console.error('failed to show'); }"
    x-show="show"
    x-transition:enter="transition ease-out duration-300"
    x-transition:enter-start="opacity-0 transform translate-y-full"
    x-transition:enter-end="opacity-100 transform translate-y-0"
    x-transition:leave="transition ease-in duration-200"
    x-transition:leave-start="opacity-100 transform translate-x-0"
    x-transition:leave-end="opacity-0 transform translate-x-full"
    class="{{ className }} fixed bottom-5 right-5"
    role="alert"
>
  {{ content }}
</div>
{# def
    type: str = "default",
    title: str = "",
    description: str = "",
    width = "min-w-96"
#}
{% set type_class = {
    "default": "bg-white text-zinc-900 border border-zinc-200 dark:bg-zinc-950 dark:text-zinc-50 dark:border-zinc-700",
    "success": "bg-green-100 text-green-900 border border-green-200 dark:bg-green-900 dark:text-green-100 dark:border-green-800",
    "error": "bg-red-100 text-red-900 border border-red-200 dark:bg-red-900 dark:text-red-100 dark:border-red-800",
    "warning": "bg-yellow-100 text-yellow-900 border border-yellow-200 dark:bg-yellow-900 dark:text-yellow-100 dark:border-yellow-800",
    "destructive": "border-red-500 bg-red-500 text-zinc-50 dark:border-red-900 dark:bg-red-900 dark:text-zinc-50"
  }[type] %}
<div class="{{ type_class }} w-full max-w-md rounded-md p-4 shadow-lg">
  <button
      @click="show = false"
      class="absolute right-2 top-2 text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300"
  >
    <XIcon alt="Close" class="h-4 w-4"/>
  </button>
  <div class="pr-6 {{ width }}">
    <p class="font-medium">{{ title }}</p>
    <p class="text-sm ">{{ description }}</p>
  </div>
</div>
{# def
  className: str = "",
  variant: str = "default",
  toast_target: str,
#}

{% set click_action="$dispatch('show-toast', '" + toast_target + "')" %}
<Button :@click="{{ click_action }}" class="{{ className }}" :variant="{{ variant }}">
  {{ content }}
</Button>

Examples

Success

<ToastTrigger variant="outline" toast_target="toast-success">Show Success Toast</ToastTrigger>

<Toast id="toast-success">
  <ToastContent
      type="success"
      title="Success!"
      description="Your operation was successful."
  />
</Toast>

Warning

<ToastTrigger variant="outline" toast_target="toast-warning">Show Warning Toast</ToastTrigger>

<Toast id="toast-warning">
  <ToastContent
      type="warning"
      title="Warning!"
      description="This is your warning."
  />
</Toast>

Error

<ToastTrigger variant="outline" toast_target="toast-error">Show Error Toast</ToastTrigger>

<Toast id="toast-error">
  <ToastContent
      type="error"
      title="Error!"
      description="An error has occurred."
  />
</Toast>

Destructive

<ToastTrigger variant="outline" toast_target="toast-warning">Show Toast</ToastTrigger>

<Toast id="toast-warning">
  <ToastContent
      type="destructive"
      title="Uh oh! Something went wrong."
      description="There was a problem with your request."
  />
</Toast>