How to create a notification with Tailwind CSS and Alpinejs

Learn how to create a notification with Tailwind CSS and Alpinejs, including how to use Alpine.js directives to add and remove notifications dynamically

Published on September 26, 2025 by Michael Andreuzza

Creating a Notification with Tailwind CSS and Alpine.js

Notifications are useful for showing messages to users, like success confirmations or error alerts. This tutorial shows how to build a simple notification using Tailwind CSS for styling and Alpine.js for handling the show/hide behavior.

Basic Setup

First, include the notification component in your HTML. Here’s the full code:

<div
  class="w-full max-w-xl mx-auto"
  x-data="{
    open: false,
    type: 'success',
    title: 'Success!',
    message: 'Coffee has been successfully refilled.',
    timeout: 4000,
    timer: null,
    show(payload = {}) {
      const d = { type:'success', title:this.title, message:this.message, timeout:this.timeout, ...payload };
      this.type = d.type; this.title = d.title; this.message = d.message; this.timeout = d.timeout;
      this.open = true;
      this.startTimer();
    },
    close(){ this.open = false; this.clearTimer(); },
    clearTimer(){ if(this.timer){ clearTimeout(this.timer); this.timer = null; } },
    startTimer(){ this.clearTimer(); if(this.timeout !== false){ this.timer = setTimeout(()=> this.close(), Number(this.timeout)||4000); } }
  }"
  x-init="window.addEventListener('notify', (e) => show(e.detail||{}))"
>
  <!-- Demo buttons -->
  <div class="flex flex-wrap items-center justify-center gap-2">
    <button
      class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
      @click="show({ type:'success', title:'Saved', message:'Changes stored.' })"
    >
      Success
    </button>
    <button
      class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
      @click="show({ type:'info', title:'Heads up', message:'New version available.' })"
    >
      Info
    </button>
    <button
      class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
      @click="show({ type:'warning', title:'Check access', message:'Some users lost permissions.' })"
    >
      Warning
    </button>
    <button
      class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
      @click="show({ type:'error', title:'Sync failed', message:'Could not reach the server.' })"
    >
      Error
    </button>
  </div>

  <!-- Notification panel -->
  <div
    aria-live="assertive"
    class="fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:items-start sm:p-6"
  >
    <div class="flex flex-col items-center w-full space-y-4 sm:items-end">
      <div
        x-show="open"
        x-transition:enter="transform ease-out duration-300"
        x-transition:enter-start="translate-y-2 opacity-0 sm:translate-x-2 sm:translate-y-0"
        x-transition:enter-end="translate-y-0 opacity-100 sm:translate-x-0"
        x-transition:leave="transition ease-in duration-150"
        x-transition:leave-start="opacity-100"
        x-transition:leave-end="opacity-0"
        @mouseenter="clearTimer()"
        @mouseleave="startTimer()"
        class="w-full max-w-sm bg-white rounded-lg shadow-lg pointer-events-auto border border-gray-200"
        role="status"
        aria-live="polite"
      >
        <div class="p-4">
          <div class="flex items-start">
            <div class="shrink-0">
              <!-- Icon based on type -->
              <div x-show="type==='success'" class="text-green-500">✓</div>
              <div x-show="type==='info'" class="text-blue-500">ℹ</div>
              <div x-show="type==='warning'" class="text-yellow-500">⚠</div>
              <div x-show="type==='error'" class="text-red-500">✕</div>
            </div>
            <div class="ml-3 w-0 flex-1 pt-0.5">
              <p class="text-sm font-medium text-gray-900" x-text="title"></p>
              <p class="mt-1 text-sm text-gray-500" x-text="message"></p>
            </div>
            <div class="flex ml-4 shrink-0">
              <button
                type="button"
                @click="close()"
                class="inline-flex text-gray-400 hover:text-gray-500"
              >
                <span class="sr-only">Close</span>

              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

How It Works

The notification uses Alpine.js to control when it shows and hides. The x-data attribute sets up the data and functions:

  • open: Controls if the notification is visible.
  • type: The type of notification (success, info, warning, error).
  • title and message: The text to show.
  • timeout: How long before it auto-closes (in milliseconds).
  • show(): Function to display the notification with custom options.
  • close(): Function to hide it.
  • startTimer() and clearTimer(): Handle the auto-close timer.

Triggering Notifications

You can show notifications in two ways:

  1. With buttons: Click the demo buttons to see different types.
<button
  @click="show({ type:'success', title:'Saved', message:'Changes stored.' })"
>
  Success
</button>
  1. From JavaScript: Use a custom event to trigger it from anywhere.
window.dispatchEvent(
  new CustomEvent("notify", {
    detail: {
      type: "success",
      title: "Saved",
      message: "All good",
      timeout: 4000,
    },
  })
);

Customizing the Notification

Change the type, title, message, or timeout when calling show():

show({
  type: "error",
  title: "Error",
  message: "Something went wrong",
  timeout: 5000,
});

Set timeout: false to keep it open until manually closed.

Animations

The notification slides in and fades out using Tailwind’s transition classes. It pauses the timer when you hover over it, so you can read the message.

This setup gives you a flexible way to show user feedback without reloading the page.

/Michael Andreuzza

Did you like this post? Please share it with your friends!