CoreUI is in beta - APIs and components may change.
CoreUI
Base Components

Tag

Efficient tool for categorizing and labeling content within your application.


Tag

Installation

Install the following dependencies:

npm install @radix-ui/react-slot

Create a tag.tsx file and paste the following code into it.

components/ui/tag.tsx
// CoreUI Tag v0.0.0import * as React from 'react';import { Slot } from '@radix-ui/react-slot';import { RiCloseFill } from '@remixicon/react';import { PolymorphicComponentProps } from '@/utils/polymorphic';import { recursiveCloneChildren } from '@/utils/recursive-clone-children';import { tv, type VariantProps } from '@/utils/tv';const TAG_ROOT_NAME = 'TagRoot';const TAG_ICON_NAME = 'TagIcon';const TAG_DISMISS_BUTTON_NAME = 'TagDismissButton';const TAG_DISMISS_ICON_NAME = 'TagDismissIcon';export const tagVariants = tv({  slots: {    root: [      'group/tag inline-flex h-6 items-center gap-2 rounded-md px-2 text-label-xs text-text-sub-600',      'transition duration-200 ease-out',      'ring-1 ring-inset',    ],    icon: [      // base      '-mx-1 size-4 shrink-0 text-text-soft-400 transition duration-200 ease-out',      // hover      'group-hover/tag:text-text-sub-600',    ],    dismissButton: [      // base      'group/dismiss-button -ml-1.5 -mr-1 size-4 shrink-0',      // focus      'focus:outline-none',    ],    dismissIcon: 'size-4 text-text-soft-400 transition duration-200 ease-out',  },  variants: {    variant: {      stroke: {        root: [          // base          'bg-bg-white-0 ring-stroke-soft-200',          // hover          'hover:bg-bg-weak-50 hover:ring-transparent',          // focus-within          'focus-within:bg-bg-weak-50 focus-within:ring-transparent',        ],        dismissIcon: [          // hover          'group-hover/dismiss-button:text-text-sub-600',          // focus          'group-focus/dismiss-button:text-text-sub-600',        ],      },      gray: {        root: [          // base          'bg-bg-weak-50 ring-transparent',          // hover          'hover:bg-bg-white-0 hover:ring-stroke-soft-200',        ],      },    },    disabled: {      true: {        root: 'pointer-events-none bg-bg-weak-50 text-text-disabled-300 ring-transparent',        icon: 'text-text-disabled-300 [&:not(.remixicon)]:opacity-[.48]',        dismissIcon: 'text-text-disabled-300',      },    },  },  defaultVariants: {    variant: 'stroke',  },});type TagSharedProps = VariantProps<typeof tagVariants>;type TagProps = VariantProps<typeof tagVariants> &  React.HTMLAttributes<HTMLDivElement> & {    asChild?: boolean;  };const TagRoot = React.forwardRef<HTMLDivElement, TagProps>(  (    { asChild, children, variant, disabled, className, ...rest },    forwardedRef,  ) => {    const uniqueId = React.useId();    const Component = asChild ? Slot : 'div';    const { root } = tagVariants({ variant, disabled });    const sharedProps: TagSharedProps = {      variant,      disabled,    };    const extendedChildren = recursiveCloneChildren(      children as React.ReactElement[],      sharedProps,      [TAG_ICON_NAME, TAG_DISMISS_BUTTON_NAME, TAG_DISMISS_ICON_NAME],      uniqueId,      asChild,    );    return (      <Component        ref={forwardedRef}        className={root({ class: className })}        aria-disabled={disabled}        {...rest}      >        {extendedChildren}      </Component>    );  },);TagRoot.displayName = TAG_ROOT_NAME;function TagIcon<T extends React.ElementType>({  className,  variant,  disabled,  as,  ...rest}: PolymorphicComponentProps<T, TagSharedProps>) {  const Component = as || 'div';  const { icon } = tagVariants({ variant, disabled });  return <Component className={icon({ class: className })} {...rest} />;}TagIcon.displayName = TAG_ICON_NAME;type TagDismissButtonProps = TagSharedProps &  React.ButtonHTMLAttributes<HTMLButtonElement> & {    asChild?: boolean;  };const TagDismissButton = React.forwardRef<  HTMLButtonElement,  TagDismissButtonProps>(  (    { asChild, children, className, variant, disabled, ...rest },    forwardedRef,  ) => {    const Component = asChild ? Slot : 'button';    const { dismissButton } = tagVariants({ variant, disabled });    return (      <Component        ref={forwardedRef}        className={dismissButton({ class: className })}        {...rest}      >        {children ?? (          <TagDismissIcon            variant={variant}            disabled={disabled}            as={RiCloseFill}          />        )}      </Component>    );  },);TagDismissButton.displayName = TAG_DISMISS_BUTTON_NAME;function TagDismissIcon<T extends React.ElementType>({  className,  variant,  disabled,  as,  ...rest}: PolymorphicComponentProps<T, TagSharedProps>) {  const Component = as || 'div';  const { dismissIcon } = tagVariants({ variant, disabled });  return <Component className={dismissIcon({ class: className })} {...rest} />;}TagDismissIcon.displayName = TAG_DISMISS_ICON_NAME;export {  TagRoot as Root,  TagIcon as Icon,  TagDismissButton as DismissButton,  TagDismissIcon as DismissIcon,};

Examples

Stroke (Default)

Tag
Customer

Gray

Tag
Customer

Disabled

Tag
Customer
Tag
Customer

With Image

Apex
Figma

With Avatar

James Brown

Dismissable

Tag
Customer
Tag
Customer

API Reference

Tag.Root

This component is based on the <div> element and supports all of its props. And adds:

PropTypeDefault
variant"stroke" | "gray""stroke"
disabledboolean
asChildboolean

Tag.Icon

The Tag.Icon component is polymorphic, allowing you to change the underlying HTML element using the as prop.

PropTypeDefault
asReact.ElementTypediv

Tag.DismissButton

This component is based on the <button> element and supports all of its props. And adds:

PropTypeDefault
asChildboolean

Tag.DismissIcon

The Tag.DismissIcon component is polymorphic, allowing you to change the underlying HTML element using the as prop.

PropTypeDefault
asReact.ElementTypediv

On this page