Skip to Content
Hashti Design System
Components

GridList

Interactive collection view for structured item lists with multi-selection, drag-and-drop reordering, and keyboard grid navigation.

GridList

A GridList represents an interactive list of items where each element can be navigated, selected, reordered, and manipulated with assistive technologies. While a standard <Table> is designed for tabular column records, a GridList excels at single-column or card-style collection views, message inboxes, file directories, and task queues.

Built on React Aria Components’ GridList, Hashti elevates this pattern with built-in selection checkboxes, drag-and-drop grab handles, empty state slots, and three density tiers.

GridListMulti-selection collection with drag-and-drop handles.
01 Darvazeh Entrance Arch
02 Hashti Octagonal Chamber
03 Dalan Bent Corridor
04 Hayati Sunken Courtyard

Import

import { GridList } from "@hashti/react";
// Or named subcomponents:
// GridList.Root, GridList.Item, GridList.DragHandle, GridList.Checkbox, GridList.EmptyState

Anatomy

<GridList selectionMode="multiple" aria-label="Inbox messages">
  <GridList.Item textValue="System notification">
    <GridList.DragHandle />
    <GridList.Checkbox />
    <span>Build #402 deployed to production</span>
  </GridList.Item>
</GridList>

Examples

Selectable List with Checkboxes

A multi-selection list featuring checkboxes and keyboard selection:

import { useState } from "react";
import { GridList, Badge } from "@hashti/react";
import type { Selection } from "react-aria-components";

const tasks = [
  { id: "1", title: "Migrate documentation to Fumadocs", status: "completed" },
  { id: "2", title: "Audit WCAG 2.2 AAA contrast across 8 themes", status: "in-progress" },
  { id: "3", title: "Add Persian Vazirmatn typography specimen", status: "completed" },
  { id: "4", title: "Release RAC 1.22.1 elevated components", status: "pending" },
];

export function SelectableTasksList() {
  const [selected, setSelected] = useState<Selection>(new Set(["1", "3"]));

  return (
    <div className="max-w-lg space-y-3">
      <GridList
        aria-label="Design system tasks"
        items={tasks}
        selectionMode="multiple"
        selectedKeys={selected}
        onSelectionChange={setSelected}
        variant="bordered"
      >
        {(task) => (
          <GridList.Item key={task.id} textValue={task.title}>
            <div className="flex items-center justify-between w-full py-1">
              <div className="flex items-center gap-3">
                <GridList.Checkbox />
                <span className="text-sm font-medium">{task.title}</span>
              </div>
              <Badge
                kind={
                  task.status === "completed"
                    ? "success"
                    : task.status === "in-progress"
                      ? "info"
                      : "neutral"
                }
              >
                {task.status}
              </Badge>
            </div>
          </GridList.Item>
        )}
      </GridList>

      <p className="text-xs text-[var(--text-muted)]">
        Selected tasks count: {selected === "all" ? tasks.length : selected.size}
      </p>
    </div>
  );
}

Visual Variants

Hashti provides three container surface styles:

  • bordered (default): Encased in a single clean border with internal divider rules.
  • flat: Minimalist borderless item rows with subtle hover washes.
  • card: Floating raised card rows separated by discrete margins.
import { GridList } from "@hashti/react";

export function CardGridListExample() {
  return (
    <GridList variant="card" aria-label="Server clusters" selectionMode="single">
      <GridList.Item textValue="Cluster US-East">
        <div>
          <p className="font-semibold text-sm">us-east-prod-01</p>
          <p className="text-xs text-[var(--text-muted)]">32 nodes · 99.98% uptime</p>
        </div>
      </GridList.Item>
      <GridList.Item textValue="Cluster EU-West">
        <div>
          <p className="font-semibold text-sm">eu-west-prod-02</p>
          <p className="text-xs text-[var(--text-muted)]">16 nodes · 100% uptime</p>
        </div>
      </GridList.Item>
    </GridList>
  );
}

Drag-and-Drop Reordering

Integrate <GridList.DragHandle /> to enable intuitive touch, mouse, and keyboard item reordering:

import { GridList } from "@hashti/react";

export function ReorderableQueueList() {
  return (
    <GridList aria-label="Playback queue" variant="bordered">
      <GridList.Item textValue="Track 1">
        <div className="flex items-center gap-2">
          <GridList.DragHandle />
          <span>01. Introduction to Safavid Geometry</span>
        </div>
      </GridList.Item>
      <GridList.Item textValue="Track 2">
        <div className="flex items-center gap-2">
          <GridList.DragHandle />
          <span>02. The Sacred Threshold of Hashti</span>
        </div>
      </GridList.Item>
    </GridList>
  );
}

Empty State Handling

When no items match search criteria or the collection is empty, supply emptyContent:

<GridList
  aria-label="Search results"
  items={[]}
  emptyContent={
    <div className="text-center py-8">
      <p className="font-medium text-sm">No items found</p>
      <p className="text-xs text-[var(--text-muted)]">Try adjusting your active filters</p>
    </div>
  }
>
  {(item) => <GridList.Item>{item.name}</GridList.Item>}
</GridList>

Props Reference

GridList (GridList.Root)

PropTypeDefaultDescription
selectionMode'none' | 'single' | 'multiple''none'Allowed selection behavior for items.
selectedKeys'all' | Iterable<Key>—Set of currently selected item keys (controlled).
defaultSelectedKeys'all' | Iterable<Key>—Initial selected keys (uncontrolled).
onSelectionChange(keys: Selection) => void—Callback fired when the selection changes.
size'sm' | 'md' | 'lg''md'Height density and internal item padding.
variant'bordered' | 'flat' | 'card''bordered'Container background and border styling.
emptyContentReactNode—Fallback content rendered when items is empty.
aria-labelstring—Accessible description of the collection.

GridList.Item (GridListItem)

PropTypeDefaultDescription
idKey—Unique key for the item within the collection.
textValuestring—Plain text string representing item content for typeahead and accessibility.
isDisabledbooleanfalseDisables interaction and selection for this item.
childrenReactNode—Content, drag handle, or checkbox.

GridList.DragHandle (GridListDragHandle)

PropTypeDefaultDescription
aria-labelstring'Reorder item'Accessible label for the drag handle button.

Keyboard Interactions

Hashti GridList follows the WAI-ARIA Grid Pattern:

KeyAction
↓ / ↑Navigates between items in the list.
SpaceToggles selection of the focused item.
Shift + ↓ / ↑Extends selection range across multiple items.
⌘ + A / Ctrl + ASelects all items in multiple selection mode.
EnterTriggers the primary action on the focused item.
Home / EndMoves focus directly to the first or last item in the list.

Hashti Token Bindings

TokenCSS VariableApplied State
Surface Normal--surfaceItem default background
Surface Hover--surface-hoverRow hover highlight
Selection Surface--selected (#e7edff)Selected item tint
Focus Ring--focus2px focus border on active item
Border Divider--dividerSeparator line between rows

Keyboard Interactions

KeyAction
↓ / ↑Moves keyboard focus between grid list items.
SpaceToggles item selection in selectable mode.
⌘A / Ctrl+ASelects all items in multiple selection mode.

WAI-ARIA & Data Attributes

AttributeTargetDescription
[data-hovered]ItemPresent when pointer hovers over item.
[data-selected]ItemSet on selected list items.
[data-focused]ItemActive on keyboard focus.
[data-disabled]ItemPresent on disabled items.