Tag Group

A tag group is a focusable list of labels, categories, keywords, filters, or other items, with support for keyboard navigation, selection, and removal.


Priority
High
Med
Low

Installation

Install via VScode or copy and paste the code below into a new file, preferably in a folder called components/base.

BaseLayer

import {
  Label as AriaLabel,
  Tag as AriaTag,
  TagGroupProps as AriaTagGroupProps,
  TagProps as AriaTagProps,
  TagGroup,
  TagList,
  TagListProps,
  Text,
} from "react-aria-components";
import { tv } from "tailwind-variants";
import { Button } from "@/components/base/button";

const tagGroup = tv({
  slots: {
    root: "flex flex-col gap-2 text-sm",
    list: "flex flex-wrap gap-2",
    tag: "flex cursor-default items-center rounded-md border p-2 outline-none ring-focus ring-offset-2 ring-offset-surface aria-selected:bg-secondary aria-selected:text-secondary-fg data-[focus-visible]:ring-2",
  },
});

const { root, list, tag } = tagGroup();

const Label = AriaLabel;

interface TagGroupProps<T>
  extends Omit<AriaTagGroupProps, "children">,
    Pick<TagListProps<T>, "items" | "children" | "renderEmptyState"> {
  label?: string;
  description?: string;
  errorMessage?: string;
}

const TagGroupRoot = <T extends object>({
  label,
  className,
  description,
  errorMessage,
  items,
  children,
  renderEmptyState,
  ...props
}: TagGroupProps<T>) => (
  <TagGroup className={root({ className })} {...props}>
    <AriaLabel>{label}</AriaLabel>
    <TagList
      className={list()}
      items={items}
      renderEmptyState={renderEmptyState}
    >
      {children}
    </TagList>
    {description && (
      <Text className="text-sm" slot="description">
        {description}
      </Text>
    )}
    {errorMessage && (
      <Text className="text-sm text-critical" slot="errorMessage">
        {errorMessage}
      </Text>
    )}
  </TagGroup>
);

const Tag = ({
  children,
  className,
  ...props
}: AriaTagProps & { className?: string }) => {
  let textValue = typeof children === "string" ? children : undefined;
  return (
    <AriaTag className={tag({ className })} textValue={textValue} {...props}>
      {({ allowsRemoving }) => (
        <>
          {children}
          {allowsRemoving && <Button slot="remove"></Button>}
        </>
      )}
    </AriaTag>
  );
};

export { Label, Tag, TagGroupRoot };

Examples

Default