Skip to content

Banner ​

The strip over a view: one line and the buttons that answer it. Anything that waits for a person goes here, such as a conflict, a failure or a request. A toast in the corner is read after the fact, and a decision has to be where the eyes already are.

tsx
import { Banner } from '@basmilius/react-ui';
tsx
import { useState } from 'react';
import { CircleAlert, GitMerge } from 'lucide-react';
import { Banner, Button } from '@basmilius/react-ui';

export default function BannerDemo() {
    const [resolved, setResolved] = useState(false);

    return (
        <div className="relative flex h-48 w-full items-end justify-center rounded-lg bg-surface-sunken p-4">
            {resolved ? (
                <Banner icon={CircleAlert} tone="neutral" message="The merge finished." />
            ) : (
                <Banner icon={GitMerge} tone="attention" message="Two files have conflicts.">
                    <Button size="sm" variant="primary" onClick={() => setResolved(true)}>
                        Resolve
                    </Button>
                    <Button size="sm">Abort</Button>
                </Banner>
            )}
            <Button size="sm" variant="secondary" onClick={() => setResolved(false)}>
                Start over
            </Button>
        </div>
    );
}

By default it stands centered along the top of its positioned parent. Pass a className to put it somewhere else; it replaces the default placement. Show one at a time: the slot is one row, and the caller decides what stands in it. The banner is a polite live region, so a screen reader announces a new line.

Props ​

PropTypeDefault
iconLucideIconRequired.
toneBannerToneRequired. 'attention' | 'error' | 'neutral' colors the icon.
messageReactNodeRequired.
childrenReactNodeThe buttons.
classNamestring'absolute inset-x-0 top-3 z-20'Where the strip stands.
refRef<HTMLDivElement>

BannerProps and BannerTone are exported types.