# Tag

A tag is a representation of a value that someone has picked, like recipients for an email or categories on a planner task.

## Best practices

### Do

- Enable dismissal when people need to undo their choices.
- Make an interaction tag’s primary action directly relate to the object it represents.
- Allow tags to wrap when people need to see, edit, or remove every tag.

### Don't

- Include actions that are irrelevant or disruptive to the current task.
- Truncate tag content.

## Behavior and states

- Tags are optionally dismissible.
- Non-dismissible tags communicate that a selection isn’t editable in the current context.
- Tags reflow onto the next line by default when they can’t fit the container.
- Static tags have no interaction states; dismissible tags enable hover and focus states.
- Tags create a visual pattern that lets people know they can change or remove the selection.
- To show system-generated data that people can’t change, use a badge instead.

## Default

Source: CoreAI.

[Download source](/coreai-design-system/source/examples/components-tag-tag--default.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { TagProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import * as React from 'react';

export const Default = (props: Partial<TagProps>): JSXElement => <Tag {...props}>Primary text</Tag>;

export default Default;
```

## API

Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. `?` means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.

### Tag API

Tag component -  a visual representation of an attribute.
Provides visual attributes such as media, icon, primary and secondary text, as well as the ability to attach an action (by default it is dismiss)

```ts
interface TagProps {
  /**
   * A Tag can have filled, outlined or brand experience.
   * @default 'filled'
   */
  appearance?: "brand" | "filled" | "outline";

  as?: "button" | "span";

  children?: any;

  className?: string;

  /**
   * A Tag can show that it cannot be interacted with.
   * @default false
   */
  disabled?: boolean;

  /**
   * A Tag can be dismissible
   * @default false
   */
  dismissible?: boolean;

  /**
   * Slot for the dismiss icon
   */
  dismissIcon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  /**
   * Slot for an icon
   */
  icon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  /**
   * Slot for a visual element, usually an avatar
   */
  media?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  /**
   * Main text for the Tag. Children of the root slot are automatically rendered here
   */
  primaryText?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  ref?: Ref<HTMLSpanElement>;

  /**
   * Secondary text that describes or complements the main text
   */
  secondaryText?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  /**
   * An InteractionTag can be selected.
   * Note: This prop only changes the appearance of the tag at the moment. A future PR will add the integration with TagGroup.
   * @default false
   */
  selected?: boolean;

  /**
   * A Tag can have rounded or circular shape.
   * @default 'round'
   */
  shape?: "circular" | "rounded";

  /**
   * A Tag has three sizes.
   * @default 'medium'
   */
  size?: "small" | "medium" | "extra-small";

  style?: CSSProperties;

  /**
   * Unique value identifying the tag within a TagGroup
   */
  value?: string | (readonly string[] & string);
}
```

## Implementation

CoreAI 0.12.0; Fluent 9.74.9.

[Setup and dependencies](/coreai-design-system/guides/Package.md).

Import components from `@coreai-ds/react-components`. CoreAI guidance above takes precedence over API demonstration examples.

## Appearance

Source: Fluent, adapted for CoreAI.

A tag can have a `filled`, `outline` or `brand` appearance. The default is `filled`.

[Download source](/coreai-design-system/source/examples/components-tag-tag--appearance.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';

const useContainerStyles = makeStyles({
    container: {
        columnGap: '10px',
        display: 'flex',
        flexWrap: 'wrap',
    },
});

export const Appearance = (): JSXElement => {
    const styles = useContainerStyles();
    return (<div className={styles.container}>
      <Tag icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        filled
      </Tag>
      <Tag appearance="outline" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        outline
      </Tag>
      <Tag appearance="brand" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        brand
      </Tag>
    </div>);
};

export default Appearance;
```

## Disabled

Source: Fluent, adapted for CoreAI.

[Download source](/coreai-design-system/source/examples/components-tag-tag--disabled.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';

const useContainerStyles = makeStyles({
    container: {
        columnGap: '10px',
        display: 'flex',
        flexWrap: 'wrap',
    },
});

export const Disabled = (): JSXElement => {
    const styles = useContainerStyles();
    return (<div className={styles.container}>
      <Tag disabled secondaryText="appearance=filled" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        Disabled
      </Tag>
      <Tag disabled secondaryText="appearance=outline" appearance="outline" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        Disabled
      </Tag>
      <Tag disabled secondaryText="appearance=brand" appearance="brand" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        Disabled
      </Tag>
    </div>);
};

export default Disabled;
```

## Dismiss

Source: Fluent, adapted for CoreAI.

A tag can have a dismiss icon and become focusable. TagGroup can handle dismiss for a collection of tags. Ensure that focus is properly managed when all tags have been dismissed.

[Download source](/coreai-design-system/source/examples/components-tag-tag--dismiss.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import * as React from 'react';
import type { TagGroupProps } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { TagGroup } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";

const initialTags = [
    { value: '1', children: 'Tag 1' },
    { value: '2', children: 'Tag 2' },
    { value: '3', children: 'Tag 3' },
];

/**
 * focus management for the reset button
 */
const useResetExample = (visibleTagsLength: number) => {
    const resetButtonRef = React.useRef<HTMLButtonElement>(null);
    const firstTagRef = React.useRef<HTMLButtonElement>(null);
    const prevVisibleTagsLengthRef = React.useRef<number>(visibleTagsLength);
    React.useEffect(() => {
        if (visibleTagsLength === 0) {
            resetButtonRef.current?.focus();
        }
        else if (prevVisibleTagsLengthRef.current === 0) {
            firstTagRef.current?.focus();
        }
        prevVisibleTagsLengthRef.current = visibleTagsLength;
    }, [visibleTagsLength]);
    return { firstTagRef, resetButtonRef };
};

const useStyles = makeStyles({
    container: {
        display: 'flex',
        flexDirection: 'column',
        rowGap: '10px',
    },
    tagGroup: {
        flexWrap: 'wrap',
    },
    resetButton: {
        width: 'fit-content',
    },
});

export const Dismiss = (): JSXElement => {
    const [visibleTags, setVisibleTags] = React.useState(initialTags);
    const removeItem: TagGroupProps['onDismiss'] = (_e, { value }) => {
        setVisibleTags([...visibleTags].filter(tag => tag.value !== value));
    };
    const resetItems = () => setVisibleTags(initialTags);
    const { firstTagRef, resetButtonRef } = useResetExample(visibleTags.length);
    const styles = useStyles();
    return (<div className={styles.container}>
      {visibleTags.length !== 0 && (<TagGroup className={styles.tagGroup} onDismiss={removeItem} aria-label="Dismiss example">
          {visibleTags.map((tag, index) => (<Tag dismissible dismissIcon={{ 'aria-label': 'remove' }} value={tag.value} key={tag.value} ref={index === 0 ? firstTagRef : null}>
              {tag.children}
            </Tag>))}
        </TagGroup>)}

      <Button onClick={resetItems} ref={resetButtonRef} disabled={visibleTags.length !== 0} className={styles.resetButton} size="small">
        Reset the example
      </Button>
    </div>);
};

export default Dismiss;
```

## Icon

Source: Fluent, adapted for CoreAI.

A Tag can render a custom icon if provided.

[Download source](/coreai-design-system/source/examples/components-tag-tag--icon.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';

export const Icon = (): JSXElement => <Tag icon={<CalendarMonthRegular />}>Primary text</Tag>;

export default Icon;
```

## Media

Source: Fluent, adapted for CoreAI.

A tag can render a media, for example an Avatar.

[Download source](/coreai-design-system/source/examples/components-tag-tag--media.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import * as React from 'react';

export const Media = (): JSXElement => (<Tag media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>Primary text</Tag>);

export default Media;
```

## SecondaryText

Source: Fluent, adapted for CoreAI.

A Tag can have a secondary text.

[Download source](/coreai-design-system/source/examples/components-tag-tag--secondary-text.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import * as React from 'react';

export const SecondaryText = (): JSXElement => <Tag secondaryText="Secondary text">Primary text</Tag>;

export default SecondaryText;
```

## Selected

Source: Fluent, adapted for CoreAI.

[Download source](/coreai-design-system/source/examples/components-tag-tag--selected.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { makeResetStyles } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';

const useContainerStyles = makeResetStyles({
    columnGap: '10px',
    display: 'flex',
});

export const Selected = (): JSXElement => {
    const containerStyles = useContainerStyles();
    return (<div className={containerStyles}>
      <Tag selected secondaryText="appearance=filled" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        Selected
      </Tag>
      <Tag selected secondaryText="appearance=outline" appearance="outline" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        Selected
      </Tag>
      <Tag selected secondaryText="appearance=brand" appearance="brand" icon={<CalendarMonthRegular />} dismissible dismissIcon={{ 'aria-label': 'remove' }}>
        Selected
      </Tag>
    </div>);
};

export default Selected;
```

## Shape

Source: Fluent, adapted for CoreAI.

A tag can be rounded or circular,

[Download source](/coreai-design-system/source/examples/components-tag-tag--shape.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';

const useContainerStyles = makeStyles({
    innerWrapper: {
        alignItems: 'start',
        columnGap: '10px',
        display: 'flex',
        flexWrap: 'wrap',
    },
    outerWrapper: {
        display: 'flex',
        flexDirection: 'column',
        rowGap: '10px',
    },
});

export const Shape = (): JSXElement => {
    const containerStyles = useContainerStyles();
    return (<div className={containerStyles.outerWrapper}>
      <div className={containerStyles.innerWrapper}>
        <Tag media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>Rounded</Tag>
        <Tag shape="circular" media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
          Circular
        </Tag>
      </div>

      <div className={containerStyles.innerWrapper}>
        <Tag dismissible dismissIcon={{ 'aria-label': 'remove' }} icon={<CalendarMonthRegular />} secondaryText="Secondary text">
          Rounded
        </Tag>
        <Tag shape="circular" dismissible dismissIcon={{ 'aria-label': 'remove' }} icon={<CalendarMonthRegular />} secondaryText="Secondary text">
          Circular
        </Tag>
      </div>
    </div>);
};

export default Shape;
```

## Size

Source: Fluent, adapted for CoreAI.

A tag supports `medium`, `small` and `extra-small` size. Default size is `medium`.

[Download source](/coreai-design-system/source/examples/components-tag-tag--size.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Tag } from "@coreai-ds/react-components";
import { Avatar } from "@coreai-ds/react-components";
import { CalendarMonthRegular } from "@fluentui/react-icons";
import * as React from 'react';

const useContainerStyles = makeStyles({
    innerWrapper: {
        alignItems: 'start',
        columnGap: '10px',
        display: 'flex',
        flexWrap: 'wrap',
    },
    outerWrapper: {
        display: 'flex',
        flexDirection: 'column',
        rowGap: '10px',
    },
});

export const Size = (): JSXElement => {
    const styles = useContainerStyles();
    return (<div className={styles.outerWrapper}>
      {/* medium */}
      <div className={styles.innerWrapper}>
        <Tag>Medium</Tag>
        <Tag dismissible dismissIcon={{ 'aria-label': 'remove' }} media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
          Medium dismissible
        </Tag>
        <Tag icon={<CalendarMonthRegular />} shape="circular">
          Medium circular
        </Tag>
      </div>

      {/* small */}
      <div className={styles.innerWrapper}>
        <Tag size="small">Small</Tag>
        <Tag dismissible dismissIcon={{ 'aria-label': 'remove' }} size="small" media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
          Small dismissible
        </Tag>
        <Tag size="small" icon={<CalendarMonthRegular />} shape="circular">
          Small circular
        </Tag>
      </div>

      {/* extra-small */}
      <div className={styles.innerWrapper}>
        <Tag size="extra-small">Extra small</Tag>
        <Tag dismissible dismissIcon={{ 'aria-label': 'remove' }} size="extra-small" media={<Avatar name="Katri Athokas" badge={{ status: 'busy' }}/>}>
          Extra small dismissible
        </Tag>
        <Tag size="extra-small" icon={<CalendarMonthRegular />} shape="circular">
          Extra small circular
        </Tag>
      </div>
    </div>);
};

export default Size;
```

## Resources

- [Figma component](https://www.figma.com/design/eXfSQrRg3poHRgmr0ivN5V/CoreAI-Figma-Library?node-id=174111-15541)
- [Fluent guidance](https://fluent2.microsoft.design/components/web/react/core/tag/usage)