Spotlight
One slide at a time with permanently visible side arrows. Uses classes.main to carve
out gutters and classes.slideControls with !opacity-100 to keep the arrows always visible.
Real-time Analytics
Monitor KPIs with live dashboards updated every second.
Enterprise Security
End-to-end encryption with role-based access control.
500+ Integrations
Connect any tool in your stack with pre-built connectors.
Cloud Native
Deploy anywhere with auto-scaling and zero-downtime updates.
import { HvCard, HvCardContent, HvCardHeader, HvCarousel, HvCarouselSlide, HvStatusIcon, HvTypography, } from "@hitachivantara/uikit-react-core"; type SlideVariant = "success" | "warning" | "info" | "default"; const SLIDES: { icon: string; variant: SlideVariant; title: string; desc: string; }[] = [ { icon: "i-ph-chart-line", variant: "success", title: "Real-time Analytics", desc: "Monitor KPIs with live dashboards updated every second.", }, { icon: "i-ph-shield-check", variant: "warning", title: "Enterprise Security", desc: "End-to-end encryption with role-based access control.", }, { icon: "i-ph-plugs-connected", variant: "info", title: "500+ Integrations", desc: "Connect any tool in your stack with pre-built connectors.", }, { icon: "i-ph-cloud", variant: "default", title: "Cloud Native", desc: "Deploy anywhere with auto-scaling and zero-downtime updates.", }, ]; export default function Spotlight() { return ( <HvCarousel showSlideControls showDots controlsPosition="bottom" carouselOptions={{ loop: true }} className="w-[600px]" classes={{ root: "bg-transparent", main: "px-xl py-xs", slideControls: "!opacity-100", slidesViewport: "pb-xs", dot: "h-4px w-4px rounded-full", dotSelected: "!w-[20px] !h-[8px]", controls: "border-none", }} > {SLIDES.map((slide) => ( <HvCarouselSlide key={slide.title}> <div className="h-full mx-sm"> <HvCard bgcolor="bgContainer" className="h-full"> <HvCardHeader title={ <div className="flex items-center gap-xs"> <HvStatusIcon variant={slide.variant} customIcon={ slide.variant === "default" ? ( <div className={slide.icon} /> ) : undefined } /> <HvTypography variant="title4">{slide.title}</HvTypography> </div> } /> <HvCardContent> <HvTypography>{slide.desc}</HvTypography> </HvCardContent> </HvCard> </div> </HvCarouselSlide> ))} </HvCarousel> ); }
Metric thumbnails
Uses renderThumbnail with custom stat tiles — metric name, current value, and trend — instead
of images. onChange keeps the carousel title in sync with the active slide.
Uptime
99.97%
$1.24M
8,432
0.12%
import { useState } from "react"; import { HvCarousel, HvCarouselSlide, HvTag, HvTypography, } from "@hitachivantara/uikit-react-core"; import { HvBarChart } from "@hitachivantara/uikit-react-viz"; type Status = "positive" | "negative" | "warning"; const METRICS: { label: string; value: string; trend: string; status: Status; period: string; breakdown: { day: string; num: number; display: string }[]; }[] = [ { label: "Uptime", value: "99.97%", trend: "+0.02%", status: "positive", period: "Last 5 days", breakdown: [ { day: "Mon", num: 100.0, display: "100.00%" }, { day: "Tue", num: 99.98, display: "99.98%" }, { day: "Wed", num: 100.0, display: "100.00%" }, { day: "Thu", num: 99.91, display: "99.91%" }, { day: "Fri", num: 100.0, display: "100.00%" }, ], }, { label: "Revenue", value: "$1.24M", trend: "+12%", status: "positive", period: "Last 5 weeks", breakdown: [ { day: "Wk 1", num: 0.88, display: "$0.88M" }, { day: "Wk 2", num: 0.95, display: "$0.95M" }, { day: "Wk 3", num: 1.02, display: "$1.02M" }, { day: "Wk 4", num: 1.11, display: "$1.11M" }, { day: "Wk 5", num: 1.24, display: "$1.24M" }, ], }, { label: "Active Users", value: "8,432", trend: "-3%", status: "negative", period: "Last 5 days", breakdown: [ { day: "Mon", num: 8871, display: "8,871" }, { day: "Tue", num: 8740, display: "8,740" }, { day: "Wed", num: 8615, display: "8,615" }, { day: "Thu", num: 8500, display: "8,500" }, { day: "Fri", num: 8432, display: "8,432" }, ], }, { label: "Error Rate", value: "0.12%", trend: "+0.05%", status: "warning", period: "Last 5 days", breakdown: [ { day: "Mon", num: 0.07, display: "0.07%" }, { day: "Tue", num: 0.08, display: "0.08%" }, { day: "Wed", num: 0.09, display: "0.09%" }, { day: "Thu", num: 0.11, display: "0.11%" }, { day: "Fri", num: 0.12, display: "0.12%" }, ], }, ]; export default function MetricThumbnails() { const [active, setActive] = useState(0); return ( <HvCarousel title={METRICS[active].label} renderThumbnail={(i) => { const m = METRICS[i]; return ( <div className="flex flex-col items-start p-xs gap-2px w-full bg-bgContainer rounded border border-atmo3"> <HvTypography variant="caption2" className="text-secondary"> {m.label} </HvTypography> <HvTypography variant="title4">{m.value}</HvTypography> <HvTypography variant="caption2" className={ m.status === "positive" ? "text-positive" : m.status === "negative" ? "text-negative" : "text-warning" } > {m.trend} </HvTypography> </div> ); }} thumbnailWidth={110} onChange={setActive} classes={{ thumbnail: "rounded border border-transparent", thumbnailSelected: "border-primary!", panel: "justify-center", controls: "hidden", }} > {METRICS.map((m) => ( <HvCarouselSlide key={m.label}> <div className="flex flex-col px-sm pt-md pb-xs"> <div className="flex gap-xs items-center"> <HvTypography variant="display">{m.value}</HvTypography> <HvTag label={m.trend} color={m.status} /> <HvTypography variant="caption2" className="text-secondary ml-auto" > {m.period} </HvTypography> </div> <HvBarChart horizontal data={{ Period: m.breakdown.map((b) => b.day), [m.label]: m.breakdown.map((b) => b.num), }} groupBy="Period" measures={m.label} height={320} /> </div> </HvCarouselSlide> ))} </HvCarousel> ); }
Release notes
Uses the actions prop together with onChange to track which slides have been viewed.
The actions area shows a live unread count and a “Mark all as read” shortcut — making it
clear the slot is intended for persistent controls that coordinate with carousel state.
What's new
Dark mode & Theme builder
Full dark mode support across all components, plus a new visual theme builder to create and preview custom color palettes without leaving the browser.
500+ Data source connectors
Connect to any tool in your stack with pre-built connectors for databases, cloud storage, REST APIs, and streaming platforms. No configuration required.
AI-assisted layout generation
Describe what you need in plain language and let the AI assistant generate dashboard layouts, suggest relevant KPIs, and pre-fill sample data for rapid prototyping.
Real-time collaboration
Multiple users can now edit dashboards simultaneously. Live cursors, presence indicators, and conflict-free merging keep everyone in sync.
import { useState } from "react"; import { HvButton, HvCard, HvCardContent, HvCardHeader, HvCarousel, HvCarouselSlide, HvIconContainer, HvTag, HvTypography, } from "@hitachivantara/uikit-react-core"; const RELEASES = [ { version: "v3.2", date: "Jun 2025", icon: "i-ph-moon", title: "Dark mode & Theme builder", desc: "Full dark mode support across all components, plus a new visual theme builder to create and preview custom color palettes without leaving the browser.", }, { version: "v3.1", date: "Mar 2025", icon: "i-ph-plugs-connected", title: "500+ Data source connectors", desc: "Connect to any tool in your stack with pre-built connectors for databases, cloud storage, REST APIs, and streaming platforms. No configuration required.", }, { version: "v3.0", date: "Jan 2025", icon: "i-ph-robot", title: "AI-assisted layout generation", desc: "Describe what you need in plain language and let the AI assistant generate dashboard layouts, suggest relevant KPIs, and pre-fill sample data for rapid prototyping.", }, { version: "v2.9", date: "Oct 2024", icon: "i-ph-users-three", title: "Real-time collaboration", desc: "Multiple users can now edit dashboards simultaneously. Live cursors, presence indicators, and conflict-free merging keep everyone in sync.", }, ]; export default function ReleaseNotes() { const [read, setRead] = useState(new Set([0])); const unread = RELEASES.length - read.size; return ( <HvCarousel title="What's new" showSlideControls showDots carouselOptions={{ loop: false }} onChange={(i) => setRead((prev) => new Set([...prev, i]))} classes={{ slidesViewport: "pt-sm", main: "px-xl", slideControls: "!opacity-100", }} actions={ <div className="flex items-center gap-xs"> {unread > 0 ? ( <> <HvTypography variant="caption2">{unread} unread</HvTypography> <HvButton variant="secondaryGhost" size="sm" onClick={() => setRead(new Set(RELEASES.map((_, i) => i)))} > Mark all as read </HvButton> </> ) : ( <div className="flex gap-xxs mr-sm items-center"> <HvTypography variant="caption2" className="text-positive"> All caught up </HvTypography> <HvIconContainer size="xs"> <div className="i-ph-check-circle text-positive" /> </HvIconContainer> </div> )} </div> } > {RELEASES.map((r, i) => ( <HvCarouselSlide key={r.version}> <HvCard bgcolor="bgContainer" className="h-full mx-sm"> <HvCardHeader title={ <div className="flex items-center gap-xs"> <HvIconContainer> <div className={`${r.icon}`} /> </HvIconContainer> <HvTypography variant="title4">{r.title}</HvTypography> </div> } subheader={ <div className="flex items-center gap-xs"> <HvTag label={r.version} color="info" /> <HvTypography variant="caption2">{r.date}</HvTypography> {!read.has(i) && ( <span className="size-6px rounded-full bg-brand" /> )} </div> } /> <HvCardContent> <HvTypography className="text-secondary">{r.desc}</HvTypography> </HvCardContent> </HvCard> </HvCarouselSlide> ))} </HvCarousel> ); }