# v3.0.0-beta.8 **Category**: react **URL**: https://www.heroui.com/docs/react/releases/v3-0-0-beta-8 **Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/react/releases/v3-0-0-beta-8.mdx > 3 new components (Badge, Pagination, Table), DateField improvements, and key API/style fixes. ***
March 2, 2026
This release adds three new components: [Badge](/docs/components/badge), [Pagination](/docs/components/pagination), and [Table](/docs/components/table), plus new `InputContainer` composition APIs for [DateField](/docs/components/date-field) and [TimeField](/docs/components/time-field). ⚠️ **Breaking changes**: TextField CSS classes were renamed from `.text-field` to `.textfield`. ## Installation Update to the latest version: ```bash npm i @heroui/styles@beta @heroui/react@beta ``` ```bash pnpm add @heroui/styles@beta @heroui/react@beta ``` ```bash yarn add @heroui/styles@beta @heroui/react@beta ``` ```bash bun add @heroui/styles@beta @heroui/react@beta ``` **Using AI assistants?** Simply prompt "Hey Cursor, update HeroUI to the latest version" and your AI assistant will automatically compare versions and apply the necessary changes. Learn more about the [HeroUI MCP Server](/docs/ui-for-agents/mcp-server). ## What's New ### New Components - **[Badge](#badge)**: Compact status + count indicator with color, variant, placement, and size options. ([Docs](/docs/components/badge)) - **[Pagination](#pagination)**: Compound pagination primitives with summary, ellipsis, and previous/next controls. ([Docs](/docs/components/pagination)) - **[Table](#table)**: Data table primitives with sorting, selection, resizing, async loading, and footer composition. ([Docs](/docs/components/table)) ### Badge New badge primitives for counters, labels, and anchored overlays with `Badge.Anchor` and `Badge.Label`. ```tsx import {Avatar, Badge} from "@heroui/react"; const GREEN_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"; const ORANGE_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"; const BLUE_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"; export function BadgeBasic() { return (
JD 5 AB New CD
); } ``` ### Pagination New navigation component built with composable parts (`Root`, `Content`, `Item`, `Link`, `Previous`, `Next`, `Summary`, `Ellipsis`). ```tsx "use client"; import {Pagination} from "@heroui/react"; import {useState} from "react"; export function PaginationWithEllipsis() { const [page, setPage] = useState(1); const totalPages = 12; const getPageNumbers = () => { const pages: (number | "ellipsis")[] = []; pages.push(1); if (page > 3) { pages.push("ellipsis"); } const start = Math.max(2, page - 1); const end = Math.min(totalPages - 1, page + 1); for (let i = start; i <= end; i++) { pages.push(i); } if (page < totalPages - 2) { pages.push("ellipsis"); } pages.push(totalPages); return pages; }; return (
setPage((p) => p - 1)}> Previous {getPageNumbers().map((p, i) => p === "ellipsis" ? ( ) : ( setPage(p)}> {p} ), )} setPage((p) => p + 1)}> Next
); } ``` ### Table Compound data table on React Aria with sortable columns, row selection, custom cells, load-more sentinel rows, and resizable columns. ```tsx import {Table} from "@heroui/react"; export function Basic() { return ( Name Role Status Email Kate Moore CEO Active kate@acme.com John Smith CTO Active john@acme.com Sara Johnson CMO On Leave sara@acme.com Michael Brown CFO Active michael@acme.com
); } ``` **Custom Cells:** ```tsx "use client"; import type {Selection, SortDescriptor} from "@heroui/react"; import {Avatar, Button, Checkbox, Chip, Table, cn} from "@heroui/react"; import {Icon} from "@iconify/react"; import {useMemo, useState} from "react"; interface User { id: number; name: string; image_url: string; role: string; status: "Active" | "Inactive" | "On Leave"; email: string; } const statusColorMap: Record = { Active: "success", Inactive: "danger", "On Leave": "warning", }; const users: User[] = [ { email: "kate@acme.com", id: 4586932, image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg", name: "Kate Moore", role: "Chief Executive Officer", status: "Active", }, { email: "john@acme.com", id: 5273849, image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg", name: "John Smith", role: "Chief Technology Officer", status: "Active", }, { email: "sara@acme.com", id: 7492836, image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg", name: "Sara Johnson", role: "Chief Marketing Officer", status: "On Leave", }, { email: "michael@acme.com", id: 8293746, image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg", name: "Michael Brown", role: "Chief Financial Officer", status: "Active", }, { email: "emily@acme.com", id: 1234567, image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg", name: "Emily Davis", role: "Product Manager", status: "Inactive", }, ]; function SortableColumnHeader({ children, sortDirection, }: { children: React.ReactNode; sortDirection?: "ascending" | "descending"; }) { return ( {children} {!!sortDirection && ( )} ); } export function CustomCells() { const [selectedKeys, setSelectedKeys] = useState(new Set()); const [sortDescriptor, setSortDescriptor] = useState({ column: "name", direction: "ascending", }); const sortedUsers = useMemo(() => { return [...users].sort((a, b) => { const col = sortDescriptor.column as keyof User; const first = String(a[col]); const second = String(b[col]); let cmp = first.localeCompare(second); if (sortDescriptor.direction === "descending") { cmp *= -1; } return cmp; }); }, [sortDescriptor]); return ( {({sortDirection}) => ( Worker ID )} {({sortDirection}) => ( Member )} {({sortDirection}) => ( Role )} {({sortDirection}) => ( Status )} Actions {sortedUsers.map((user) => (
#{user.id.toString()}{" "}
{user.name .split(" ") .map((n) => n[0]) .join("")}
{user.name} {user.email}
{user.role} {user.status}
))}
); } ``` **Pagination:** ```tsx "use client"; import {Pagination, Table} from "@heroui/react"; import {useMemo, useState} from "react"; const columns = [ {id: "name", name: "Name"}, {id: "role", name: "Role"}, {id: "status", name: "Status"}, {id: "email", name: "Email"}, ]; const users = [ {email: "kate@acme.com", id: 1, name: "Kate Moore", role: "CEO", status: "Active"}, {email: "john@acme.com", id: 2, name: "John Smith", role: "CTO", status: "Active"}, {email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "CMO", status: "On Leave"}, {email: "michael@acme.com", id: 4, name: "Michael Brown", role: "CFO", status: "Active"}, { email: "emily@acme.com", id: 5, name: "Emily Davis", role: "Product Manager", status: "Inactive", }, {email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "Lead Designer", status: "Active"}, { email: "olivia@acme.com", id: 7, name: "Olivia Martinez", role: "Frontend Engineer", status: "Active", }, { email: "james@acme.com", id: 8, name: "James Taylor", role: "Backend Engineer", status: "Active", }, ]; const ROWS_PER_PAGE = 4; export function PaginationDemo() { const [page, setPage] = useState(1); const totalPages = Math.ceil(users.length / ROWS_PER_PAGE); const pages = Array.from({length: totalPages}, (_, i) => i + 1); const paginatedItems = useMemo(() => { const start = (page - 1) * ROWS_PER_PAGE; return users.slice(start, start + ROWS_PER_PAGE); }, [page]); const start = (page - 1) * ROWS_PER_PAGE + 1; const end = Math.min(page * ROWS_PER_PAGE, users.length); return ( {(column) => ( {column.name} )} {(user) => ( {(column) => {user[column.id as keyof typeof user]}} )} {start} to {end} of {users.length} results setPage((p) => Math.max(1, p - 1))} > Prev {pages.map((p) => ( setPage(p)}> {p} ))} setPage((p) => Math.min(totalPages, p + 1))} > Next
); } ``` **Empty State:** ```tsx "use client"; import {EmptyState, Table} from "@heroui/react"; import {Icon} from "@iconify/react"; export function EmptyStateDemo() { return ( Name Role Status Email ( No results found )} > {[]}
); } ``` ## Component + API Improvements ### DateField and TimeField Enhancements `DateField` and `TimeField` now expose `InputContainer` to wrap input segments between prefix and suffix content. **Before:** ```tsx ... {(segment) => } ... ``` **After:** ```tsx ... {(segment) => } ... ``` ```tsx "use client"; import type {TimeValue} from "@heroui/react"; import {DateField, DateRangePicker, Label, RangeCalendar, TimeField} from "@heroui/react"; import {getLocalTimeZone, parseZonedDateTime} from "@internationalized/date"; export function InputContainer() { const localTimeZone = getLocalTimeZone(); const defaultValue = { end: parseZonedDateTime(`2026-02-10T18:45:00[${localTimeZone}]`), start: parseZonedDateTime(`2026-02-03T08:45:00[${localTimeZone}]`), }; return ( {({state}) => ( <> {(segment) => } {(segment) => } {(day) => {day}} {(date) => } {({year}) => }
state.setTimeRange({ end: state.timeRange?.end as TimeValue, start: v as TimeValue, }) } > {(segment) => }
state.setTimeRange({ end: v as TimeValue, start: state.timeRange?.start as TimeValue, }) } > {(segment) => }
)}
); } ``` ## ⚠️ Breaking Changes ### TextField Class + Path Renames TextField style naming was normalized to avoid conflicts with Tailwind's `text-*` utility prefix. | Component | Old Class Name | New Class Name | Notes | |-----------|---------------|----------------|-------| | **TextField Root** | `.text-field` | `.textfield` | Root class renamed | | **TextField Full Width** | `.text-field--full-width` | `.textfield--full-width` | Modifier class renamed | Additional renames in the same change: - Style file: `text-field.css` -> `textfield.css` - Styles export path: `@heroui/styles/src/components/text-field` -> `@heroui/styles/src/components/textfield` ## Style Fixes - **RangeCalendar**: Added rounded corners to calendar cells for range selection visuals ([#6270](https://github.com/heroui-inc/heroui/pull/6270)) ## Bug Fixes - Added required-state red asterisk behavior for **DatePicker** and **DateRangePicker** by propagating `data-required` from `isRequired` ([#6270](https://github.com/heroui-inc/heroui/pull/6270)) - Fixed missing invalid trigger styling in **Autocomplete** and **Select** by scoping invalid styles to root state ([#6270](https://github.com/heroui-inc/heroui/pull/6270)) - Updated TextField docs/demo references to the new `textfield-*` demo keys and source/style paths ([#6270](https://github.com/heroui-inc/heroui/pull/6270)) ## Links - [Component Documentation](/docs/components) - [Design System - Figma Kit V3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3) - [GitHub Repository](https://github.com/heroui-inc/heroui) - [GitHub PR #6270](https://github.com/heroui-inc/heroui/pull/6270) ## Contributors Thanks to everyone who contributed to this release!