PromoBanner

Dismissible announcement banner at top of page.

Usage

import { PromoBanner } from '@/components/landing';

// In your page
const showBanner = true;

<>
  {showBanner && <PromoBanner />}
  <Navbar hasBanner={showBanner} />
</>

Location: src/components/landing/PromoBanner.tsx

Features

Integration with Navbar

When using PromoBanner, pass hasBanner={true} to Navbar for proper positioning:

export default function LandingPage() {
  const [showBanner, setShowBanner] = useState(true);

  return (
    <div className="min-h-screen">
      {showBanner && (
        <PromoBanner onDismiss={() => setShowBanner(false)} />
      )}
      <Navbar hasBanner={showBanner} />
      <Hero />
    </div>
  );
}

Props

Prop Type Default Description
onDismiss () => void - Callback when banner is dismissed

Customization

i18n

Update translations:

{
  "landing": {
    "promoBanner": {
      "text": "New: Check out our latest feature release",
      "linkText": "Learn more",
      "linkUrl": "/changelog"
    }
  }
}

Example Implementation

'use client';

import { useState } from 'react';
import Link from 'next/link';
import { useTranslations } from 'next-intl';
import { XMarkIcon } from '@/components/icons';

interface PromoBannerProps {
  onDismiss?: () => void;
}

export default function PromoBanner({ onDismiss }: PromoBannerProps) {
  const t = useTranslations('landing.promoBanner');
  const [isVisible, setIsVisible] = useState(true);

  const handleDismiss = () => {
    setIsVisible(false);
    onDismiss?.();
  };

  if (!isVisible) return null;

  return (
    <div className="bg-zinc-900 dark:bg-zinc-800 text-white py-2 px-4">
      <div className="container mx-auto flex items-center justify-center gap-4">
        <p className="text-sm">
          {t('text')}{' '}
          <Link
            href={t('linkUrl')}
            className="underline hover:no-underline font-medium"
          >
            {t('linkText')} &rarr;
          </Link>
        </p>
        <button
          onClick={handleDismiss}
          className="p-1 hover:bg-zinc-800 dark:hover:bg-zinc-700 rounded"
          aria-label="Dismiss banner"
        >
          <XMarkIcon className="w-4 h-4" />
        </button>
      </div>
    </div>
  );
}

Persistence

To remember dismissal across sessions:

import { useState, useEffect } from 'react';

export default function PromoBanner() {
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const dismissed = localStorage.getItem('promo-banner-dismissed');
    setIsVisible(!dismissed);
  }, []);

  const handleDismiss = () => {
    setIsVisible(false);
    localStorage.setItem('promo-banner-dismissed', 'true');
  };

  if (!isVisible) return null;

  return (
    // ... banner content
  );
}

Styling Variants

Gradient Background

className="bg-gradient-to-r from-blue-600 to-purple-600 text-white"

With Icon

<div className="flex items-center gap-2">
  <SparklesIcon className="w-4 h-4" />
  <span>{t('text')}</span>
</div>

Countdown Timer

For limited-time offers:

<span className="font-mono">
  Ends in: {days}d {hours}h {minutes}m
</span>