Nexus DSdocs

Bubble

How do I rotate the signing key?
Run nexus keys rotate, then redeploy.

Installation

npm install @radix-ui/react-slot@^1.2.4 class-variance-authority@^0.7.1 clsx@^2.1.1 tailwind-merge@^3.4.0
lib/utils.ts
components/bubble/bubble.tsx
components/bubble/index.ts

Code

'use client';

import { Bubble, BubbleContent, BubbleGroup } from '@/components/bubble/bubble';

export default function BubbleDemo() {
  return (
    <BubbleGroup className="nx:w-full nx:max-w-sm">
      <Bubble align="start">
        <BubbleContent>How do I rotate the signing key?</BubbleContent>
      </Bubble>
      <Bubble variant="primary" align="end">
        <BubbleContent>Run nexus keys rotate, then redeploy.</BubbleContent>
      </Bubble>
    </BubbleGroup>
  );
}

Props

Bubble

Bubble props
PropTypeDefaultDescription
align"end" | "start" | nullNo default documentedNo description
variant"destructive" | "outline" | "ghost" | "muted" | "primary" | null"muted"No description

BubbleContent

BubbleContent props
PropTypeDefaultDescription
asChildbooleanfalse

Render as the child element instead of a div, merging BubbleContent's styles onto it. Use when the message body needs different semantics — a p for prose, or an a / button when the whole turn is actionable.

An interactive child takes the design-system focus ring and the pointer cursor, and still owns its own accessible name. Both are withheld from a body that cannot be actioned — an a with no href, a disabled button, or anything carrying aria-disabled.

The turn itself carries no hover tint or press cue: the surface stays still and the affordance reads from the body — the underline on a link, the label on a button. Add one at the call site if a product surface needs it.

BubbleGroup

BubbleGroup adds no props of its own; it accepts the props of the element or primitive it wraps.

BubbleReactions

BubbleReactions props
PropTypeDefaultDescription
align"end" | "start" | null"end"No description
side"bottom" | "top" | null"bottom"No description

Examples

Variants

Muted is the default incoming surface.
Primary marks your own messages.
Outline keeps the turn quiet.
Ghost drops the surface entirely.
Destructive flags a failed turn.
'use client';

import { Bubble, BubbleContent, BubbleGroup } from '@/components/bubble/bubble';

export default function BubbleVariants() {
  return (
    <BubbleGroup className="nx:w-full nx:max-w-sm">
      <Bubble variant="muted">
        <BubbleContent>Muted is the default incoming surface.</BubbleContent>
      </Bubble>
      <Bubble variant="primary" align="end">
        <BubbleContent>Primary marks your own messages.</BubbleContent>
      </Bubble>
      <Bubble variant="outline">
        <BubbleContent>Outline keeps the turn quiet.</BubbleContent>
      </Bubble>
      <Bubble variant="ghost">
        <BubbleContent>Ghost drops the surface entirely.</BubbleContent>
      </Bubble>
      <Bubble variant="destructive">
        <BubbleContent>Destructive flags a failed turn.</BubbleContent>
      </Bubble>
    </BubbleGroup>
  );
}

With reactions

Landed the marker primitive.
🎉 3
Reviewing it now.
👀 1
'use client';

import {
  Bubble,
  BubbleContent,
  BubbleGroup,
  BubbleReactions,
} from '@/components/bubble/bubble';

export default function BubbleWithReactions() {
  return (
    <BubbleGroup className="nx:w-full nx:max-w-sm">
      <Bubble align="start">
        <BubbleContent>Landed the marker primitive.</BubbleContent>
        <BubbleReactions>🎉 3</BubbleReactions>
      </Bubble>
      <Bubble variant="primary" align="end">
        <BubbleContent>Reviewing it now.</BubbleContent>
        <BubbleReactions align="start">👀 1</BubbleReactions>
      </Bubble>
    </BubbleGroup>
  );
}