Tag
View .mdA tag is a representation of a value that someone has picked, like recipients for an email or categories on a planner task.
Default
Loading interactive example. Full source is available below.
View example source
// 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;
Resources
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.
API referenceGenerated CoreAI prop and type summaries
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)
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.
Import components from @coreai-ds/react-components. CoreAI guidance above takes precedence over API demonstration examples.
Appearance
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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;