RankedList
Top products, channels or regions as an ordered list, each row a label, its value and a bar relative to the leader. The bars are decorative; the values are printed.
Import
import { RankedList } from '@nizivo/react/ranked-list';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/card.css and @nizivo/react/css/inline-error.css and @nizivo/react/css/number-value.css and @nizivo/react/css/skeleton.css
import '@nizivo/react/css/ranked-list.css';Static or interactive
Server. It ships no client JavaScript when a Server Component renders it, in every Next.js build
mode; its code is bundled only when a client component (a 'use client' file) imports it. The bars are SVG with percentage widths, so the server HTML has no style attribute.
How to choose.
In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.
Examples
Basic
Figma's "Top channels": five rows, bars relative to the leader, "View all" as a real link.
Top channels
View all- Online store$64,120
- Instagram Shop$21,804
- Wholesale$18,240
- Pop-up market$9,312
- Marketplace$6,050
import type { ComponentPropsWithRef, JSX } from 'react';
import { RankedList } from '@nizivo/react/ranked-list';
/** Stands in for a router link (Next `Link`, React Router `Link`). */
function Link({ children, ...props }: ComponentPropsWithRef<'a'>): JSX.Element {
return <a {...props}>{children}</a>;
}
const CHANNELS = [
['Online store', 64120],
['Instagram Shop', 21804],
['Wholesale', 18240],
['Pop-up market', 9312],
['Marketplace', 6050],
] as const;
const USD = { style: 'currency', currency: 'USD', maximumFractionDigits: 0 } as const;
export function RankedListExample(): JSX.Element {
return (
<RankedList.Root
title="Top channels"
action={<RankedList.Action render={<Link href="/channels" />}>View all</RankedList.Action>}
format={USD}
>
{CHANNELS.map(([label, value]) => (
<RankedList.Item key={label} label={label} value={value} />
))}
</RankedList.Root>
);
}
States
The Suspense fallback is the same list with status="loading": it reserves five rows, so the box does not move when the data streams in (§16.5). Empty and error keep the same box.
Top channels
Top cities
Top products
import type { JSX } from 'react';
import { RankedList } from '@nizivo/react/ranked-list';
export function RankedListStatesExample(): JSX.Element {
return (
<div>
<RankedList.Root title="Top channels" status="loading" />
<RankedList.Root title="Top cities" status="empty" />
<RankedList.Root title="Top products" status="error" />
</div>
);
}
Parts
Use the parts through the namespace (RankedList.Root) or by their flat names (RankedListRoot); both work from Server Components.
RankedList.Root
Server component Renders data-slot="ranked-list" with the class nz-ranked-list. Props: RankedListRootProps. Source: src/ranked-list/ranked-list.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
action | ReactNode | – | The header's end, for example |
children | ReactNode | – |
|
empty | ReactNode | – | Replaces the empty message. |
error | ReactNode | – | Replaces the error message, for example an |
format | Intl.NumberFormatOptions | – | Every item's number format, unless the item sets its own ( |
labels | Partial<RankedListLabels> | – | |
locale | string | 'en-US' | BCP 47 tag for every item's value. |
max | number | the largest value among the direct `RankedList.Item` children (the leader); pass it when the items are wrapped in your own components | The value a full bar stands for. |
pending | boolean | false | A refresh is in flight while the rows show: |
render | RenderProp<RankedListRootState> | – | Composition of the root (Card's surface through the |
rows | number | 5 | How many rows loading, empty and error reserve, so the box does not change when the data arrives. Pass the number of rows you show (Figma: 5; "max ~6 rows, then View all"). |
status | DataStatus"ready""loading""empty""error" | 'ready' | The data lifecycle (§16.5). A ready list with no items is |
title | ReactNode | – | The heading ("Top channels"); it also names the list. |
titleLevel | RankedListTitleLevel23456 | 3 | The heading level; its look is Heading/H4 at every level. |
- Also takes every attribute of
<div>excepttitle;childrenis redeclared above. (Omit<ComponentPropsWithRef<"div">, "title">)
render takes an element, or, in client code, a function of the props and this state (RankedListRootState):
| Name | Type | Default | Description |
|---|---|---|---|
pendingrequired | boolean | – | |
statusrequired | DataStatus"ready""loading""empty""error" | – |
RankedList.Item
Server component Renders data-slot="ranked-list-item" with the class nz-ranked-list-item. Props: RankedListItemProps. Source: src/ranked-list/ranked-list.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
format | Intl.NumberFormatOptions | – | Overrides the list's |
labelrequired | ReactNode | – | |
locale | string | the list's `locale`, else 'en-US' | |
max | number | – | Overrides the list's |
render | RenderProp<RankedListItemState> | – | |
valuerequired | number | – |
- Also takes every attribute of
<li>exceptchildren;valueis redeclared above. (Omit<ComponentPropsWithRef<"li">, "children">)
render takes an element, or, in client code, a function of the props and this state (RankedListItemState):
| Name | Type | Default | Description |
|---|---|---|---|
ratiorequired | number | – | value ÷ max, from 0 to 1. |
RankedList.Action
"View all": a real link, at least 24 px tall (WCAG 2.5.8; figma/04 issue 7).
Server component Renders data-slot="ranked-list-action" with the class nz-ranked-list-action. Props: RankedListActionProps. Source: src/ranked-list/ranked-list.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
render | RenderProp<Record<string, never>> | – | The router's link, for example |
- Also takes every attribute of
<a>. (ComponentPropsWithRef<"a">)
render takes an element, or, in client code, a function of the props and no state.
Other exports
RankedListActionPropstypeimport type { RankedListActionProps } from '@nizivo/react/ranked-list'The props of
RankedList.Action, listed above.RankedListItemPropstypeimport type { RankedListItemProps } from '@nizivo/react/ranked-list'The props of
RankedList.Item, listed above.RankedListItemStatetypeimport type { RankedListItemState } from '@nizivo/react/ranked-list'The render state of
RankedList.Item, listed above.RankedListLabelstypeimport type { RankedListLabels } from '@nizivo/react/ranked-list'RankedList's own strings (§16.4): serialisable, English defaults, Metric's wording.
{label}is replaced by thetitle(left out when there is none).Name Type Default Description emptyrequiredstring'No data'Shown when there is nothing to rank and no
emptyslot.errorrequiredstring'Could not load {label}'The InlineError message on a load failure with no
errorslot.loadingrequiredstring'Loading {label}'Static, visually hidden text while loading (§16.5: no live region).
RankedListRootPropstypeimport type { RankedListRootProps } from '@nizivo/react/ranked-list'The props of
RankedList.Root, listed above.RankedListRootStatetypeimport type { RankedListRootState } from '@nizivo/react/ranked-list'The render state of
RankedList.Root, listed above.RankedListTitleLeveltypeimport type { RankedListTitleLevel } from '@nizivo/react/ranked-list'type RankedListTitleLevel = 2 | 3 | 4 | 5 | 6
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
ranked-list.nz-ranked-list | RankedList.Rootroot (takes render) | none |
ranked-list-action.nz-ranked-list-action | RankedList.Actionroot (takes render); interactive, focus: self | none |
ranked-list-bar.nz-ranked-list-bar | RankedList.Iteminner element (CSS only) | none |
ranked-list-header.nz-ranked-list-header | RankedList.Rootinner element (CSS only) | none |
ranked-list-item.nz-ranked-list-item | RankedList.Itemroot (takes render) | none |
ranked-list-label.nz-ranked-list-label | RankedList.Iteminner element (CSS only) | none |
ranked-list-list.nz-ranked-list-list | RankedList.Rootinner element (CSS only) | none |
ranked-list-message.nz-ranked-list-message | RankedList.Rootinner element (CSS only) | none |
ranked-list-placeholder.nz-ranked-list-placeholder | RankedList.Rootinner element (CSS only) | none |
ranked-list-status.nz-ranked-list-status | RankedList.Rootinner element (CSS only) | none |
ranked-list-title.nz-ranked-list-title | RankedList.Rootinner element (CSS only) | none |
ranked-list-track.nz-ranked-list-track | RankedList.Iteminner element (CSS only) | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-ranked-list-action-fg | --nz-text-brand |
|
--nz-ranked-list-bar-fill | --nz-chart-primary |
|
--nz-ranked-list-empty-fg | --nz-text-secondary |
|
--nz-ranked-list-gap | --nz-space-4 |
|
--nz-ranked-list-label-fg | --nz-text-primary |
|
--nz-ranked-list-title-font | --nz-font-heading-h4 |
|
--nz-ranked-list-track-bg | --nz-chart-track |
|
--nz-ranked-list-track-size | --nz-space-1-5 |
|
--nz-ranked-list-value-fg | --nz-text-primary |
|
Layers: nz.components. Classes: .nz-card, .nz-ranked-list, .nz-ranked-list-action, .nz-ranked-list-bar, .nz-ranked-list-empty, .nz-ranked-list-fill, .nz-ranked-list-header, .nz-ranked-list-item, .nz-ranked-list-label, .nz-ranked-list-list, .nz-ranked-list-message, .nz-ranked-list-placeholder, .nz-ranked-list-reserve, .nz-ranked-list-row, .nz-ranked-list-skeleton-label, .nz-ranked-list-skeleton-value, .nz-ranked-list-status, .nz-ranked-list-title, .nz-ranked-list-track, .nz-ranked-list-value.
Tokens this stylesheet reads (20)
--nz-chart-primary, --nz-chart-track, --nz-font-body-m, --nz-font-heading-h4, --nz-font-label-m, --nz-font-numeric-m, --nz-radius-full, --nz-radius-xs, --nz-space-1, --nz-space-1-5, --nz-space-2, --nz-space-3, --nz-space-4, --nz-space-6, --nz-text-brand, --nz-text-primary, --nz-text-secondary, --nz-tracking-heading-h4, --nz-tracking-label-m, --nz-tracking-numeric-m
Accessibility
These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:
ranked-list-action: draws the focus outline itself on keyboard focus.
Keyboard
| Keys | Action |
|---|---|
| Tab, Shift+Tab | Moves to and from the RankedList.Action link ("View all"), as on any link. The rows are not Tab stops. |
| Enter | Follows the focused link. |
Source: a native <a> (RankedListAction in ranked-list.tsx, no client code), at least 24 px tall (WCAG 2.5.8).
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: Figma node
752:444 - Ghost masters: none
RadioGroup
One choice from a short list of native radios, in a fieldset with a legend. The shell is server-rendered; one small client part passes the group's name and value to each radio.
SearchField
A search or filter input in a <search> landmark, with a magnifier, a clear button and an optional shortcut hint. Inside a <form method="get"> it filters with no JavaScript; inside a Field it is labelled, described and validated by the Field. It is also the top bar's search, as a real input.