Skip to content
TopClass UI

Banner

One message for the whole page, full width above the header. It sits in the page flow, so it pushes the page down and covers nothing.

split - size not measured yet

Banner

split - size not measured yet

One message for the whole page, full width above the header. It sits in the page flow, so it pushes the page down and covers nothing.

Install

Try it

tone
dismissible

The code

banner.tsx
import { bell } from 'topclassui/icons/bell'
import { Button } from 'topclassui/button'
import { Svg } from 'topclassui/svg'
import { Banner } from 'topclassui/banner'

<Banner
  id="banner-playground"
  aria-label="Version 2 announcement"
  icon={<Svg icon={bell} size="xs" />}
  action={<Button size="sm" variant="outline" tone="neutral" render={<a href="/changelog" />}>Read the changelog</Button>}
>
  Version 2 is out, with faster builds and a new theme editor.
</Banner>

Dismissal is remembered per id in this browser, so change the id when the message changes.

Every state

9 states
Primary
Neutral
Success
Warning
Danger
Info
No icon
With action

Import from the package

topclassui/banner
import { Banner } from 'topclassui/banner'

Install once:npm i topclassui@beta zod

Examples

  • For one message the whole page should see: a release, a trial ending, planned maintenance. Put it above the header.
  • A link action is a Button with render={<a href="..." />}; a bare link gets no styling and no 44 px touch target. When the message and the action do not fit on one line, the action moves under the message.
  • Pass an icon when the tone matters: colour alone does not tell a warning from a notice.
  • dismissible and id are plain values, so a Server Component page needs no client wrapper, and only a dismissible banner ships the client part. onDismiss is a function, so only a client file can pass it.
  • The server cannot read the visitor's storage, so the next page shows a dismissed banner until the script runs and then removes it, and the page below moves up. That is why dismissible is off by default.
basic
Version 2 is out, with faster builds and a new theme editor.
Read the changelog
tones
A new plan is available.
You are viewing an archived project.
Your domain is verified.
Your trial ends in three days.
The last payment failed.
Version 2 is out.
with-action
Your trial ends in three days.
Add a payment method
The last payment failed, so deploys are paused.
Compare plans
dismissible
Version 2 is out, with faster builds and a new theme editor.
Read the changelog
Scheduled maintenance starts on Sunday at 02:00 UTC.
minimal
Scheduled maintenance starts on Sunday at 02:00 UTC.
long-action
Your workspace has used its monthly build minutes, so new deployments wait until the plan is upgraded.
Upgrade the workspace plan and resume deployments now
تجاوزت مساحة العمل دقائق البناء الشهرية، لذلك تنتظر عمليات النشر الجديدة حتى تتم ترقية الخطة.
long-text
The deploy is taking longer than usual while the static assets upload to the edge network.
Status
https://example.com/projects/topclassui/deployments/2026-10-09/logs/build-step-four-of-six.txt
In Arabic, right to left
صدر الإصدار الثاني، مع بناء أسرع ومحرر جديد للسمات.
اقرأ المزيد
تنتهي الفترة التجريبية بعد ثلاثة أيام.

Props

The props of Banner
PropTypeDefaultLimitsDescription
aria-labelstring--The name of the landmark, such as "Release announcement".
aria-labelledbystring--The id of the visible text that names the landmark.
idstring--The key the dismissal is remembered under, and the element's id. Required with dismissible.
actionReactNode--The caller's link or Button, after the message.
childrenReactNode--The message: a sentence that wraps and is never cut.
dismissiblebooleanfalse-Adds an x that hides the banner and remembers it in this browser. Requires id.
iconReactNode--Shown before the message, such as a 16 px Svg. Leave it out for none.
toneprimary | neutral | success | warning | danger | info"primary"-What the message means, which picks the soft fill and the text colour on it.

Keyboard and accessibility

The keyboard of Banner
KeysWhat it does
TabReaches the action, then the x.
EnterSpaceOn the x, hides the banner and remembers it in this browser. Focus falls to the page; move it in onDismiss to put it somewhere.
  • A banner is a region landmark, so it needs a name: pass aria-label or aria-labelledby. The type refuses a Banner with neither, and two banners on a page need two names.
  • The x is 24 px, and 44 px on a touch screen. Under forced colours a hovered x draws an outline, and the banner draws an edge in place of its fill.
  • A dismissible banner needs an id, which the type requires; it is also the element's id.

Right to left

The icon sits at the start, the action at the end, and the x at the inline end of whatever line it wraps to, with no physical class to mirror.

Server and client

Renders on the server; the interactive part is a client component that ships only when it is used.

Files: index.tsx, client.tsx.

On the chip: split - size not measured yet.