VibeUI
ComponentsDocs
VibeUI

Components with taste, that you own. Copy the source, retint the tokens, keep every line.

Library

  • All components
  • Signature pieces
  • Text effects
  • Footers

Docs

  • Introduction
  • Installation
  • Theming
  • CLI

Project

  • GitHub
  • Agents and MCP
  • Issues
  • MIT license
© 2026 VibeUI · MIT licensed
  1. Components
  2. Feedback
  3. Banner

Banner

Announcement bar with optional action. Slides in, and fades out when dismissed.

FeedbackClient component
VibeUI 0.2 is here: new tokens, 178 refined components.

Install

bash
npx @agents-npm-packages/vibeui add banner --dir ./src/components/ui

Import

tsx
import { Banner } from "@/components/ui/banner"

API

Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.

<Banner>

PropTypeDefaultDescription
children*ReactNode—Content rendered inside the component.
actionReactNode—Optional action (usually a Button) shown alongside the message.
dismissibleboolean—Shows a close button that animates the banner away.
classNamestring—Extra classes, merged with the component's own.

Source

src/components/ui/banner.tsx
"use client";

import * as React from "react";
import { X } from "@phosphor-icons/react";
import { cn } from "@/lib/utils";

/** Announcement bar with optional action. Slides in, and fades out when dismissed. */
export function Banner({
  children,
  action,
  dismissible,
  className,
}: {
  children: React.ReactNode;
  action?: React.ReactNode;
  dismissible?: boolean;
  className?: string;
}) {
  const [closing, setClosing] = React.useState(false);
  const [gone, setGone] = React.useState(false);
  if (gone) return null;

  return (
    <div
      onAnimationEnd={(e) => {
        if (e.target === e.currentTarget && closing) setGone(true);
      }}
      className={cn(
        "flex items-center justify-between gap-4 rounded-md border border-line bg-surface px-4 py-2.5 text-sm text-ink-soft shadow-quiet",
        closing ? "animate-pop-out" : "animate-fade-up",
        className,
      )}
    >
      <div className="flex min-w-0 flex-1 items-center gap-2.5">
        <span aria-hidden className="relative flex size-1.5 shrink-0">
          <span className="absolute inset-0 animate-ping-soft rounded-full bg-accent opacity-50" />
          <span className="relative size-1.5 rounded-full bg-accent" />
        </span>
        <div className="min-w-0">{children}</div>
      </div>
      <div className="flex shrink-0 items-center gap-2">
        {action}
        {dismissible ? (
          <button
            type="button"
            aria-label="Dismiss"
            onClick={() => setClosing(true)}
            className="flex size-7 items-center justify-center rounded-sm text-faint transition-[background-color,color,transform] duration-200 hover:rotate-90 hover:bg-surface-muted hover:text-ink active:scale-90"
          >
            <X size={14} weight="bold" />
          </button>
        ) : null}
      </div>
    </div>
  );
}

Exports Banner. Peer dependencies: clsx, tailwind-merge, @phosphor-icons/react.

PreviousAlertNextCallout