Propel

Banner

A prominent message strip that surfaces status or announcements with an optional icon and trailing actions.

A new version of the workspace is available
Reload to get the latest features and fixes for your projects.
Show code
import { Banner } from "@makeplane/propel/components/banner";
import { Button } from "@makeplane/propel/components/button";
import * as React from "react";

export default function BasicDemo() {
  const [visible, setVisible] = React.useState(true);

  if (!visible) return null;

  return (
    <Banner
      placement="page"
      variant="neutral"
      title="A new version of the workspace is available"
      description="Reload to get the latest features and fixes for your projects."
      actions={
        <>
          <Button stretch="auto" variant="secondary" size="xs" label="Learn more" />
          <Button stretch="auto" variant="primary" size="xs" label="Update now" />
        </>
      }
      onDismiss={() => setVisible(false)}
    />
  );
}

Installation

import { Banner } from "@makeplane/propel/components/banner";

Usage

import { Banner } from "@makeplane/propel/components/banner";
import { Button } from "@makeplane/propel/components/button";
import * as React from "react";

export default function BasicDemo() {
  const [visible, setVisible] = React.useState(true);

  if (!visible) return null;

  return (
    <Banner
      placement="page"
      variant="neutral"
      title="A new version of the workspace is available"
      description="Reload to get the latest features and fixes for your projects."
      actions={
        <>
          <Button stretch="auto" variant="secondary" size="xs" label="Learn more" />
          <Button stretch="auto" variant="primary" size="xs" label="Update now" />
        </>
      }
      onDismiss={() => setVisible(false)}
    />
  );
}

Examples

Variants

Your workspace settings were saved
A new project template is available to try
Cycles are rolling out to your workspace this week
Show code
import { Banner } from "@makeplane/propel/components/banner";

export default function VariantsDemo() {
  return (
    <div className="flex flex-col gap-3">
      <Banner placement="inline" variant="neutral" title="Your workspace settings were saved" />
      <Banner
        placement="inline"
        variant="info"
        title="A new project template is available to try"
      />
      <Banner
        placement="inline"
        variant="accent"
        title="Cycles are rolling out to your workspace this week"
      />
      <Banner
        placement="inline"
        variant="warning"
        title="Scheduled maintenance begins at 6 PM UTC"
      />
      <Banner
        placement="inline"
        variant="danger"
        title="Two projects failed to sync with the server"
      />
    </div>
  );
}

Placements

A new version of the workspace is available
Reload to get the latest features and fixes for your projects.
A new version of the workspace is available
Reload to get the latest features and fixes for your projects.
Show code
import { Banner } from "@makeplane/propel/components/banner";

export default function PlacementsDemo() {
  return (
    <div className="flex flex-col gap-4">
      <Banner
        placement="page"
        variant="info"
        title="A new version of the workspace is available"
        description="Reload to get the latest features and fixes for your projects."
      />
      <Banner
        placement="inline"
        variant="info"
        title="A new version of the workspace is available"
        description="Reload to get the latest features and fixes for your projects."
      />
    </div>
  );
}

Dismissible

Pass onDismiss to render the dedicated trailing dismiss control. The handler fires on click; the banner does not hide itself, so consumers own visibility. Don’t append a dismiss button to actions.

Invite your teammates to this project
They will get access to every cycle and work item once they join.
Show code
import { Banner } from "@makeplane/propel/components/banner";
import * as React from "react";

export default function DismissibleDemo() {
  const [visible, setVisible] = React.useState(true);

  if (!visible) return null;

  return (
    <Banner
      placement="inline"
      variant="info"
      title="Invite your teammates to this project"
      description="They will get access to every cycle and work item once they join."
      onDismiss={() => setVisible(false)}
    />
  );
}

Icon

The leading icon defaults to a variant-appropriate glyph. Pass a custom node via icon, or icon={null} to hide it.

Cycles got faster board rendering
Show code
import { Banner } from "@makeplane/propel/components/banner";
import { Icon } from "@makeplane/propel/components/icon";
import { Rocket } from "lucide-react";

export default function IconDemo() {
  return (
    <div className="flex flex-col gap-3">
      <Banner
        placement="inline"
        variant="info"
        title="Cycles got faster board rendering"
        icon={<Icon icon={Rocket} />}
      />
      <Banner
        placement="inline"
        variant="warning"
        icon={null}
        title="Scheduled maintenance begins at 6 PM UTC"
      />
    </div>
  );
}

API Reference

Banner

The ready-made banner: composes the atomic banner parts — the variant icon, the message body (`title` + `description`), trailing `actions`, and an optional trailing `onDismiss` control — so consumers pass content, not layout. Drop down to `@makeplane/propel/elements/banner` to assemble the parts directly.

PropTypeDefaultDescription
placement(required)"page" | "inline"
variant(required)"neutral" | "info" | "accent" | "warning" | "danger"
titleReactNodeThe banner's headline. Rendered as its own block above any description body.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.
iconReactNodeNode rendered before the message (inline-start). Defaults to a variant-appropriate lucide icon; pass `null` to hide it. Sized to the banner's node size. Decorative, kept out of the name.
descriptionReactNodeBody text rendered under the title.
actionsReactNodeTrailing CTA buttons, placed after the message. Per Figma the banner carries up to three CTAs (primary / secondary / tertiary). The dismiss control is a separate slot — pass `onDismiss` rather than appending a dismiss button here. Layout note: the actions group does not shrink and does not wrap, so a `page` banner assumes a wide viewport. With the full three-CTA + dismiss set, a very narrow container (a constrained sidebar/split pane, heavy zoom) can clip the trailing CTA. Prefer fewer CTAs where width is tight; phone-width layouts belong to the mobile app, not this component.
onDismiss(() => void)When set, renders the dedicated dismiss control — a ghost, icon-only `IconButton` fixed at the banner's trailing edge (after `actions`), matching Figma's always-last "Close" anatomy node. Consumers own visibility: the handler fires on click; the banner does not hide itself.