# Badge

> A small status descriptor for UI elements.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/components/badge
- Markdown: https://ui.ahmedbna.com/docs/components/badge.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/badge.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/badge.json
- Install: `npx bna-ui add badge`
- Registry dependencies: `mode-provider`, `useColorScheme`, `colors`, `useColor`, `globals`, `text`, `view`
- Preview recording: https://demo.ahmedbna.com/0058-badge-demo.PNG

---

**Example:** A basic badge showing different variants

```tsx
// components/demo/badge/badge-demo.tsx
import { Badge } from '@/components/ui/badge';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeDemo() {
  return (
    <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 12 }}>
      <Badge>Default</Badge>
      <Badge variant='secondary'>Secondary</Badge>
      <Badge variant='destructive'>Destructive</Badge>
      <Badge variant='outline'>Outline</Badge>
      <Badge variant='success'>Success</Badge>
    </View>
  );
}
```

## Installation

### CLI

```bash
npx bna-ui add badge
```

### Manual

**1.** Copy and paste the following code into your project.

```tsx
// components/ui/badge.tsx
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { CORNERS } from '@/theme/globals';
import { TextStyle, ViewStyle } from 'react-native';

type BadgeVariant =
  'default' | 'secondary' | 'destructive' | 'outline' | 'success';

interface BadgeProps {
  children: React.ReactNode;
  variant?: BadgeVariant;
  style?: ViewStyle;
  textStyle?: TextStyle;
  accessibilityLabel?: string;
}

export function Badge({
  children,
  variant = 'default',
  style,
  textStyle,
  accessibilityLabel,
}: BadgeProps) {
  const primaryColor = useColor('primary');
  const primaryForegroundColor = useColor('primaryForeground');
  const secondaryColor = useColor('secondary');
  const secondaryForegroundColor = useColor('secondaryForeground');
  const destructiveColor = useColor('destructive');
  const destructiveForegroundColor = useColor('destructiveForeground');
  const borderColor = useColor('border');
  const successColor = useColor('success');
  const successForegroundColor = useColor('successForeground');

  const getBadgeStyle = (): ViewStyle => {
    const baseStyle: ViewStyle = {
      alignItems: 'center',
      justifyContent: 'center',
      paddingVertical: 6,
      paddingHorizontal: 12,
      borderRadius: CORNERS,
    };

    switch (variant) {
      case 'secondary':
        return { ...baseStyle, backgroundColor: secondaryColor };
      case 'destructive':
        return { ...baseStyle, backgroundColor: destructiveColor };
      case 'success':
        return { ...baseStyle, backgroundColor: successColor };
      case 'outline':
        return {
          ...baseStyle,
          backgroundColor: 'transparent',
          borderWidth: 1,
          borderColor,
        };
      default:
        return { ...baseStyle, backgroundColor: primaryColor };
    }
  };

  const getTextStyle = (): TextStyle => {
    const baseTextStyle: TextStyle = {
      fontSize: 15,
      fontWeight: '500',
      textAlign: 'center',
    };

    switch (variant) {
      case 'secondary':
        return { ...baseTextStyle, color: secondaryForegroundColor };
      case 'destructive':
        return { ...baseTextStyle, color: destructiveForegroundColor };
      case 'success':
        return { ...baseTextStyle, color: successForegroundColor };
      case 'outline':
        return { ...baseTextStyle, color: primaryColor };
      default:
        return { ...baseTextStyle, color: primaryForegroundColor };
    }
  };

  const defaultAccessibilityLabel =
    typeof children === 'string' || typeof children === 'number'
      ? String(children)
      : undefined;

  return (
    <View
      accessibilityLabel={accessibilityLabel ?? defaultAccessibilityLabel}
      style={[getBadgeStyle(), style]}
    >
      <Text style={[getTextStyle(), textStyle]}>{children}</Text>
    </View>
  );
}
```

**2.** Update the import paths to match your project setup.

## Usage

```tsx
import { Badge } from '@/components/ui/badge';
```

```tsx
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="success">Success</Badge>
```

## Examples

#### Default

**Example:** Basic badges showing all available variants

```tsx
// components/demo/badge/badge-demo.tsx
import { Badge } from '@/components/ui/badge';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeDemo() {
  return (
    <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 12 }}>
      <Badge>Default</Badge>
      <Badge variant='secondary'>Secondary</Badge>
      <Badge variant='destructive'>Destructive</Badge>
      <Badge variant='outline'>Outline</Badge>
      <Badge variant='success'>Success</Badge>
    </View>
  );
}
```

#### With Icons

**Example:** Badges with icons and custom content

```tsx
// components/demo/badge/badge-icons.tsx
import { Badge } from '@/components/ui/badge';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeIcons() {
  return (
    <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 12 }}>
      <Badge>★ Featured</Badge>

      <Badge variant='success'>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
          <Text style={{ fontSize: 12 }}>✓</Text>
          <Text>Verified</Text>
        </View>
      </Badge>

      <Badge variant='destructive'>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
          <Text style={{ fontSize: 12 }}>⚠</Text>
          <Text>Alert</Text>
        </View>
      </Badge>

      <Badge variant='outline'>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
          <Text style={{ fontSize: 12 }}>🔔</Text>
          <Text>Notification</Text>
        </View>
      </Badge>
    </View>
  );
}
```

#### Notification Badges

**Example:** Small notification badges for counters and status

```tsx
// components/demo/badge/badge-notifications.tsx
import { Badge } from '@/components/ui/badge';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeNotifications() {
  return (
    <View style={{ gap: 16 }}>
      {/* Small notification counters */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
          <Text>Messages</Text>
          <Badge
            style={{
              minWidth: 20,
              height: 20,
              paddingHorizontal: 6,
              paddingVertical: 2,
            }}
            textStyle={{ fontSize: 12 }}
          >
            3
          </Badge>
        </View>

        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
          <Text>Notifications</Text>
          <Badge
            variant='destructive'
            style={{
              width: 20,
              height: 20,
              paddingHorizontal: 0,
              paddingVertical: 0,
              borderRadius: 999,
            }}
            textStyle={{ fontSize: 12 }}
          >
            12
          </Badge>
        </View>
      </View>

      {/* Dot indicators */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
          <Text>Online</Text>
          <Badge
            variant='success'
            style={{
              width: 8,
              height: 8,
              borderRadius: 4,
              paddingHorizontal: 0,
              paddingVertical: 0,
            }}
          >
            <View />
          </Badge>
        </View>

        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
          <Text>Away</Text>
          <Badge
            style={{
              backgroundColor: '#f59e0b',
              width: 8,
              height: 8,
              borderRadius: 4,
              paddingHorizontal: 0,
              paddingVertical: 0,
            }}
          >
            <View />
          </Badge>
        </View>

        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
          <Text>Offline</Text>
          <Badge
            variant='outline'
            style={{
              width: 8,
              height: 8,
              borderRadius: 4,
              paddingHorizontal: 0,
              paddingVertical: 0,
            }}
          >
            <View />
          </Badge>
        </View>
      </View>
    </View>
  );
}
```

#### Custom Styling

**Example:** Badges with custom colors and styling

```tsx
// components/demo/badge/badge-styled.tsx
import { Badge } from '@/components/ui/badge';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeStyled() {
  return (
    <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 12 }}>
      {/* Custom colors */}
      <Badge
        style={{
          backgroundColor: '#8b5cf6',
          borderRadius: 16,
        }}
        textStyle={{ color: 'white', fontWeight: '600' }}
      >
        Purple
      </Badge>

      <Badge
        style={{
          backgroundColor: '#06b6d4',
          borderRadius: 4,
        }}
        textStyle={{ color: 'white', fontSize: 13 }}
      >
        Cyan
      </Badge>

      <Badge
        style={{
          backgroundColor: '#f97316',
          borderRadius: 20,
          paddingHorizontal: 16,
          paddingVertical: 8,
        }}
        textStyle={{ color: 'white', fontWeight: 'bold' }}
      >
        Orange
      </Badge>

      {/* Gradient-like effect with shadow */}
      <Badge
        style={{
          backgroundColor: '#ec4899',
          borderRadius: 12,
          shadowColor: '#ec4899',
          shadowOffset: { width: 0, height: 2 },
          shadowOpacity: 0.3,
          shadowRadius: 4,
          elevation: 4,
        }}
        textStyle={{ color: 'white', fontWeight: '600' }}
      >
        Pink
      </Badge>

      {/* Bordered with custom style */}
      <Badge
        variant='outline'
        style={{
          borderColor: '#10b981',
          borderWidth: 2,
          borderRadius: 8,
        }}
        textStyle={{ color: '#10b981', fontWeight: '600' }}
      >
        Green
      </Badge>
    </View>
  );
}
```

#### Interactive Badges

**Example:** Badges that can be pressed or dismissed

```tsx
// components/demo/badge/badge-interactive.tsx
import { Badge } from '@/components/ui/badge';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';
import { TouchableOpacity } from 'react-native';

export function BadgeInteractive() {
  const [tags, setTags] = useState(['React', 'TypeScript', 'Expo', 'Mobile']);
  const [selectedCategory, setSelectedCategory] = useState('All');

  const categories = ['All', 'Work', 'Personal', 'Important'];

  const removeTag = (tagToRemove: string) => {
    setTags(tags.filter((tag) => tag !== tagToRemove));
  };

  return (
    <View style={{ gap: 20 }}>
      {/* Dismissible tags */}
      <View>
        <Text style={{ marginBottom: 8, fontWeight: '600' }}>
          Tags (tap to remove):
        </Text>
        <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
          {tags.map((tag) => (
            <TouchableOpacity key={tag} onPress={() => removeTag(tag)}>
              <Badge variant='secondary'>
                <View
                  style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}
                >
                  <Text>{tag}</Text>
                  <Text style={{ fontSize: 14 }}>×</Text>
                </View>
              </Badge>
            </TouchableOpacity>
          ))}
        </View>
      </View>

      {/* Selectable categories */}
      <View>
        <Text style={{ marginBottom: 8, fontWeight: '600' }}>Categories:</Text>
        <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
          {categories.map((category) => (
            <TouchableOpacity
              key={category}
              onPress={() => setSelectedCategory(category)}
            >
              <Badge
                variant={selectedCategory === category ? 'default' : 'outline'}
                style={{
                  opacity: selectedCategory === category ? 1 : 0.7,
                }}
              >
                {category}
              </Badge>
            </TouchableOpacity>
          ))}
        </View>
      </View>

      {/* Toggle badges */}
      <View>
        <Text style={{ marginBottom: 8, fontWeight: '600' }}>
          Filter Options:
        </Text>
        <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
          <TouchableOpacity>
            <Badge variant='success'>Active</Badge>
          </TouchableOpacity>
          <TouchableOpacity>
            <Badge variant='outline'>Completed</Badge>
          </TouchableOpacity>
          <TouchableOpacity>
            <Badge variant='destructive'>Archived</Badge>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
}
```

#### Sizes

**Example:** Badges in different sizes

```tsx
// components/demo/badge/badge-sizes.tsx
import { Badge } from '@/components/ui/badge';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeSizes() {
  return (
    <View style={{ gap: 16 }}>
      {/* Extra Small */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
        <Text style={{ width: 80, fontSize: 14 }}>Extra Small:</Text>
        <Badge
          style={{
            paddingHorizontal: 6,
            paddingVertical: 2,
          }}
          textStyle={{ fontSize: 11 }}
        >
          XS
        </Badge>
        <Badge
          variant='success'
          style={{
            paddingHorizontal: 6,
            paddingVertical: 2,
          }}
          textStyle={{ fontSize: 11 }}
        >
          New
        </Badge>
      </View>

      {/* Small */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
        <Text style={{ width: 80, fontSize: 14 }}>Small:</Text>
        <Badge
          style={{
            paddingHorizontal: 8,
            paddingVertical: 4,
          }}
          textStyle={{ fontSize: 12 }}
        >
          Small
        </Badge>
        <Badge
          variant='secondary'
          style={{
            paddingHorizontal: 8,
            paddingVertical: 4,
          }}
          textStyle={{ fontSize: 12 }}
        >
          Beta
        </Badge>
      </View>

      {/* Default */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
        <Text style={{ width: 80, fontSize: 14 }}>Default:</Text>
        <Badge>Default</Badge>
        <Badge variant='outline'>Outline</Badge>
      </View>

      {/* Large */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
        <Text style={{ width: 80, fontSize: 14 }}>Large:</Text>
        <Badge
          style={{
            paddingHorizontal: 16,
            paddingVertical: 8,
          }}
          textStyle={{ fontSize: 16, fontWeight: '600' }}
        >
          Large
        </Badge>
        <Badge
          variant='destructive'
          style={{
            paddingHorizontal: 16,
            paddingVertical: 8,
          }}
          textStyle={{ fontSize: 16, fontWeight: '600' }}
        >
          Important
        </Badge>
      </View>

      {/* Extra Large */}
      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
        <Text style={{ width: 80, fontSize: 14 }}>Extra Large:</Text>
        <Badge
          style={{
            paddingHorizontal: 20,
            paddingVertical: 10,
            borderRadius: 12,
          }}
          textStyle={{ fontSize: 18, fontWeight: 'bold' }}
        >
          XL Badge
        </Badge>
      </View>
    </View>
  );
}
```

#### Status Indicators

**Example:** Badges used as status indicators

```tsx
// components/demo/badge/badge-status.tsx
import { Badge } from '@/components/ui/badge';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';

export function BadgeStatus() {
  const users = [
    { name: 'John Doe', status: 'online' },
    { name: 'Jane Smith', status: 'away' },
    { name: 'Bob Johnson', status: 'offline' },
    { name: 'Alice Brown', status: 'busy' },
  ];

  const orders = [
    { id: '#1234', status: 'pending' },
    { id: '#1235', status: 'processing' },
    { id: '#1236', status: 'shipped' },
    { id: '#1237', status: 'delivered' },
    { id: '#1238', status: 'cancelled' },
  ];

  const getStatusBadge = (status: string) => {
    switch (status) {
      case 'online':
        return <Badge variant='success'>Online</Badge>;
      case 'away':
        return (
          <Badge
            style={{ backgroundColor: '#f59e0b' }}
            textStyle={{ color: 'white' }}
          >
            Away
          </Badge>
        );
      case 'busy':
        return <Badge variant='destructive'>Busy</Badge>;
      case 'offline':
        return <Badge variant='outline'>Offline</Badge>;
      case 'pending':
        return (
          <Badge
            style={{ backgroundColor: '#6b7280' }}
            textStyle={{ color: 'white' }}
          >
            Pending
          </Badge>
        );
      case 'processing':
        return (
          <Badge
            style={{ backgroundColor: '#3b82f6' }}
            textStyle={{ color: 'white' }}
          >
            Processing
          </Badge>
        );
      case 'shipped':
        return (
          <Badge
            style={{ backgroundColor: '#8b5cf6' }}
            textStyle={{ color: 'white' }}
          >
            Shipped
          </Badge>
        );
      case 'delivered':
        return <Badge variant='success'>Delivered</Badge>;
      case 'cancelled':
        return <Badge variant='destructive'>Cancelled</Badge>;
      default:
        return <Badge variant='outline'>Unknown</Badge>;
    }
  };

  return (
    <View style={{ gap: 24 }}>
      {/* User Status */}
      <View>
        <Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 12 }}>
          User Status
        </Text>
        <View style={{ gap: 8 }}>
          {users.map((user, index) => (
            <View
              key={index}
              style={{
                flexDirection: 'row',
                alignItems: 'center',
                justifyContent: 'space-between',
                paddingVertical: 4,
              }}
            >
              <Text style={{ flex: 1 }}>{user.name}</Text>
              {getStatusBadge(user.status)}
            </View>
          ))}
        </View>
      </View>

      {/* Order Status */}
      <View>
        <Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 12 }}>
          Order Status
        </Text>
        <View style={{ gap: 8 }}>
          {orders.map((order, index) => (
            <View
              key={index}
              style={{
                flexDirection: 'row',
                alignItems: 'center',
                justifyContent: 'space-between',
                paddingVertical: 4,
              }}
            >
              <Text style={{ flex: 1 }}>Order {order.id}</Text>
              {getStatusBadge(order.status)}
            </View>
          ))}
        </View>
      </View>

      {/* Priority Levels */}
      <View>
        <Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 12 }}>
          Priority Levels
        </Text>
        <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
          <Badge
            style={{ backgroundColor: '#ef4444' }}
            textStyle={{ color: 'white', fontWeight: '600' }}
          >
            High Priority
          </Badge>
          <Badge
            style={{ backgroundColor: '#f59e0b' }}
            textStyle={{ color: 'white', fontWeight: '600' }}
          >
            Medium Priority
          </Badge>
          <Badge
            style={{ backgroundColor: '#10b981' }}
            textStyle={{ color: 'white', fontWeight: '600' }}
          >
            Low Priority
          </Badge>
        </View>
      </View>
    </View>
  );
}
```

## API Reference

### Badge

A versatile badge component for displaying status, categories, or notifications.

| Prop                 | Type                                                                  | Default     | Description                                                                                    |
| -------------------- | --------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------- |
| `children`           | `ReactNode`                                                           | -           | The content to display inside the badge.                                                       |
| `variant`            | `'default' \| 'secondary' \| 'destructive' \| 'outline' \| 'success'` | `'default'` | The visual style variant of the badge.                                                         |
| `style`              | `ViewStyle`                                                           | -           | Additional styles to apply to the badge.                                                       |
| `textStyle`          | `TextStyle`                                                           | -           | Additional styles to apply to the badge text.                                                  |
| `accessibilityLabel` | `string`                                                              | -           | Accessibility label for screen readers. Defaults to the string/number `children` when omitted. |

## Accessibility

The Badge component is built with accessibility in mind:

- Uses semantic structure for screen readers
- Proper contrast ratios for all variants
- Supports dynamic text sizing
- Clear visual distinction between different states
