Tag

free

Tag labels content with a compact keyword, category, or status marker.

Preview
Dark
LabelLabel
import Tag from '@/components/ui/Tag';

export default function UsageDemo() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Tag
        prefix
        prefixClass="bg-palette-purple"
        className="rounded-full border-0 bg-palette-purple-soft text-palette-purple-soft-foreground"
      >
        Label
      </Tag>
      <Tag prefix className="bg-card" prefixClass="bg-palette-purple">Label</Tag>
    </div>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Tag

Examples

Basic

Preview
Dark
DraftDesignEngineeringResearch
import Tag from '@/components/ui/Tag';

export default function BasicDemo() {
  return (
    <div className="flex flex-wrap gap-2">
      <Tag>Draft</Tag>
      <Tag>Design</Tag>
      <Tag>Engineering</Tag>
      <Tag>Research</Tag>
    </div>
  );
}

Sizes

Preview
Dark
SmallMediumLarge
import Tag from '@/components/ui/Tag';

export default function SizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Tag size="sm">Small</Tag>
      <Tag size="md">Medium</Tag>
      <Tag size="lg">Large</Tag>
    </div>
  );
}

Affix

Preview
Dark
Default dotCustom dotPrefix iconSuffix icon
import Tag from '@/components/ui/Tag';
import { PiHash, PiPlus } from 'react-icons/pi'

export default function AffixDemo() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Tag prefix>Default dot</Tag>
      <Tag prefix prefixClass="bg-palette-purple">
        Custom dot
      </Tag>
      <Tag prefix={<PiPlus className="mr-1.5 text-sm" />}>
        Prefix icon
      </Tag>
      <Tag suffix={<PiHash className="ml-1.5 text-sm" />}>
        Suffix icon
      </Tag>
    </div>
  );
}

Status

Preview
Dark
ActiveDraftPendingFailed
import Tag from '@/components/ui/Tag';

export default function StatusDemo() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Tag prefix prefixClass="bg-success">
        Active
      </Tag>
      <Tag prefix prefixClass="bg-info">
        Draft
      </Tag>
      <Tag prefix prefixClass="bg-warning">
        Pending
      </Tag>
      <Tag prefix prefixClass="bg-destructive">
        Failed
      </Tag>
    </div>
  );
}

Colors

Preview
Dark
EmeraldRoseBlueCyanOrangeRedPurpleYellow
import Tag from '@/components/ui/Tag';

export default function CategoryDemo() {
  return (
    <div className="flex flex-wrap gap-2">
      <Tag className="border-0 bg-palette-emerald-soft text-palette-emerald-soft-foreground">
        Emerald
      </Tag>
      <Tag className="border-0 bg-palette-rose-soft text-palette-rose-soft-foreground">
        Rose
      </Tag>
      <Tag className="border-0 bg-palette-blue-soft text-palette-blue-soft-foreground">
        Blue
      </Tag>
      <Tag className="border-0 bg-palette-cyan-soft text-palette-cyan-soft-foreground">
        Cyan
      </Tag>
      <Tag className="border-0 bg-palette-orange-soft text-palette-orange-soft-foreground">
        Orange
      </Tag>
      <Tag className="border-0 bg-palette-red-soft text-palette-red-soft-foreground">
        Red
      </Tag>
      <Tag className="border-0 bg-palette-purple-soft text-palette-purple-soft-foreground">
        Purple
      </Tag>
      <Tag className="border-0 bg-palette-yellow-soft text-palette-yellow-soft-foreground">
        Yellow
      </Tag>
    </div>
  );
}

Removable

Preview
Dark
In stockFree shippingUnder $50On sale
import { useState } from 'react';
import Tag from '@/components/ui/Tag';
import CloseButton from '@/components/ui/CloseButton';

const INITIAL_FILTERS = [
  'In stock',
  'Free shipping',
  'Under $50',
  'On sale',
];

export default function RemovableDemo() {
  const [filters, setFilters] = useState(INITIAL_FILTERS);

  const remove = (value: string) => {
    setFilters((current) => current.filter((item) => item !== value));
  };

  return (
    <div className="flex min-h-8 flex-wrap items-center gap-2">
      {filters.length === 0 ? (
        <span className="text-sm text-muted-foreground">
          No filters applied
        </span>
      ) : (
        filters.map((filter) => (
          <Tag
            key={filter}
            suffix={
              <CloseButton
                resetDefaultClass
                className="ml-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground"
                aria-label={`Remove ${filter}`}
                onClick={() => remove(filter)}
              />
            }
          >
            {filter}
          </Tag>
        ))
      )}
    </div>
  );
}

API

Tag renders a span and accepts the props below plus native props for that element.

PropDescriptionTypeDefault
childrenContent shown inside the tag.ReactNode-
sizeControls padding and text size.ControlSize'md'
prefixContent before the label. Pass true for a colored dot, or a node to render it directly.boolean | ReactNodefalse
prefixClassExtra classes for the prefix dot. Applies only when prefix is true.string-
suffixContent after the label. Pass true for a colored dot, or a node to render it directly.boolean | ReactNodefalse
suffixClassExtra classes for the suffix dot. Applies only when suffix is true.string-
classNameExtra classes for the root span.string-
refRef forwarded to the root span.Ref<HTMLSpanElement>-
...nativeSpanPropsNative props for the root span.NativeTagSpanProps-

Types

type ControlSize = 'sm' | 'md' | 'lg';
type NativeTagSpanProps = Omit<ComponentPropsWithRef<'div'>, 'prefix'>;