Tags Components
A set of examples demonstrating the usage of the Tags component in various containers.
Tags on scrollable container
Tag 1
Tag 2
Tag 3
Tag 4
Tag 5
Tag 6
Tag 7
Tag 8
Tag 9
Tag 10
import { HvTag } from "@hitachivantara/uikit-react-core";
export default function Demo() {
return (
<div className="flex w-320px p-xs border-1 border-border gap-sm overflow-x-scroll scroll-auto">
<HvTag label="Tag 1" />
<HvTag label="Tag 2" />
<HvTag label="Tag 3" />
<HvTag label="Tag 4" />
<HvTag label="Tag 5" />
<HvTag label="Tag 6" />
<HvTag label="Tag 7" />
<HvTag label="Tag 8" />
<HvTag label="Tag 9" />
<HvTag label="Tag 10" />
</div>
);
}
Tags on wrapping container
Tag 1
Tag 2
Tag 3
Tag 4
Tag 5
Tag 6
Tag 7
Tag 8
Tag 9
Tag 10
import { HvTag } from "@hitachivantara/uikit-react-core";
export default function Demo() {
return (
<div className="flex w-320px p-xs border-1 border-border gap-sm flex-wrap">
<HvTag label="Tag 1" />
<HvTag label="Tag 2" />
<HvTag label="Tag 3" />
<HvTag label="Tag 4" />
<HvTag label="Tag 5" />
<HvTag label="Tag 6" />
<HvTag label="Tag 7" />
<HvTag label="Tag 8" />
<HvTag label="Tag 9" />
<HvTag label="Tag 10" />
</div>
);
}
Tags on limited container with plus indicator (resizable container)
Tag 1
Tag 2
Tag 3
Tag 4
Tag 5
Tag 6
Tag 7
Tag 8
Tag 9
Tag 10
Tag 1
Tag 2
Tag 3
Tag 4
Tag 5
Tag 6
Tag 7
Tag 8
Tag 9
Tag 10
+99
import { useEffect, useRef, useState } from "react";
import { HvTag, HvTooltip } from "@hitachivantara/uikit-react-core";
const TAGS = [
"Tag 1",
"Tag 2",
"Tag 3",
"Tag 4",
"Tag 5",
"Tag 6",
"Tag 7",
"Tag 8",
"Tag 9",
"Tag 10",
];
export default function Demo() {
const containerRef = useRef<HTMLDivElement>(null);
const measureRef = useRef<HTMLDivElement>(null);
const [visibleCount, setVisibleCount] = useState(TAGS.length);
const recalculate = () => {
const container = containerRef.current;
const measureBox = measureRef.current;
if (!container || !measureBox) return;
const tagEls = Array.from(measureBox.children) as HTMLElement[];
const containerWidth = container.offsetWidth;
const tagWidths = tagEls.map((el) => el.offsetWidth);
let totalUsed = 0;
let total = 0;
let count = 0;
for (let i = 0; i < tagWidths.length; i++) {
total += tagWidths[i] + 8; // account for gap-sm (8px)
if (total > containerWidth) break;
totalUsed += tagWidths[i] + 8; // account for gap-sm (8px)
count++;
}
// Reserve space for "+X" tag if not all fit
if (count < TAGS.length) {
const plusXWidth = 32;
if (totalUsed + plusXWidth > containerWidth) {
count--;
}
}
setVisibleCount(count - 1);
};
// Observe resize of the container
useEffect(() => {
const observer = new ResizeObserver(() => {
recalculate();
});
if (containerRef.current) {
observer.observe(containerRef.current);
}
return () => {
observer.disconnect();
};
}, []);
// Wait for styles to be loaded before measuring
useEffect(() => {
const waitForStyles = () => {
const el = measureRef.current?.children[0] as HTMLElement;
if (!el || el.offsetWidth === 0) {
requestAnimationFrame(waitForStyles);
} else {
recalculate();
}
};
waitForStyles();
}, []);
return (
<>
<div
ref={containerRef}
className="flex min-w-80px max-w-420px w-300px resize-x overflow-auto p-xs border-1 border-border gap-sm"
>
{TAGS.slice(0, visibleCount).map((label) => (
<HvTag key={label} label={label} />
))}
{visibleCount < TAGS.length && (
<HvTooltip
title={
<div className="flex items-center gap-xs">
{TAGS.slice(visibleCount).map((label) => (
<HvTag key={label} label={label} />
))}
</div>
}
>
<HvTag label={`+${TAGS.length - visibleCount}`} />
</HvTooltip>
)}
</div>
{/* Hidden tags for measurement */}
<div
className="invisible absolute top-0 left-0 flex gap-sm"
ref={measureRef}
>
{TAGS.map((label) => (
<HvTag key={label} label={label} />
))}
<HvTag label="+99" />
</div>
</>
);
}