AppLauncher
app-launcherOpens a searchable grid of application shortcuts from global navigation.
Usage
Basic usage
Frosted glass panel + search (title is placeholder) + classification capsule + icon grid; arrow keys can roam in the grid.
<AppLauncher
items={apps} // [{ id, label, icon, category, section }]
categories={categories}
title="Application"
logo={<Logo />}
actions={<IconButton>···</IconButton>}
className="h-[28rem]"
/>Section · Number of columns · Solid skin
Continuous items with the same section are grouped into one group, with automatic dividing lines between groups; columns adjusts the number of columns; variant="solid" is used where there is no base map.
<AppLauncher
items={apps} // The first 4 items section="recent" → a group of their own
columns={4}
variant="solid"
searchable={false}
title="Workbench"
/>Controlled Search
search + onSearchChange Handed to the outside: The search term can share the same true source with the route query and other panels.
External search terms:(empty)
const [q, setQ] = useState("")
<AppLauncher items={apps} search={q} onSearchChange={setQ} title="Application" />Tag · Linked items · Disabled items
badge in the corner of the hanging icon; href makes the entry <a>; disabled cannot be clicked or entered in the tab sequence.
<AppLauncher
items={[
{ id: "mail", label: "Mail", icon: <MailIcon />, badge: <Dot>9</Dot> },
{ id: "docs", label: "Documentation", icon: <DocIcon />, href: "/docs" },
{ id: "old", label: "Offline", icon: <OldIcon />, disabled: true },
]}
searchable={false}
columns={3}
/>When to use
Use AppLauncher for an application center, personal workspace, micro-app marketplace, or shortcut home page where people browse a full grid of recognizable app icons and filter by category or name.
Command is a list-based, keyboard-driven command palette whose results are commands and actions. Dock is a persistent single row of icons without categories or search. AppLauncher is the grid-based choice: large named icons arranged into optional sections for visual discovery rather than command entry.
Import
import { AppLauncher, type AppLauncherItem } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| items* | AppLauncherItem[] | — | Application entries described below. |
| categories | { key, label }[] | — | Category pills. The category row is omitted when this prop is absent. |
| category / defaultCategory | string | — | Controlled or initial uncontrolled category. undefined means all categories. |
| onCategoryChange | (key?: string) => void | — | Called when the category changes. |
| allLabel | ReactNode | "\u5168\u90e8" | Label for the all-categories pill. The built-in Chinese copy means “All.” |
| title | ReactNode | — | Heading at the upper left. A string title also becomes the search placeholder, matching macOS Launchpad. |
| logo / actions | ReactNode | — | Logo before the title and actions at the upper right. |
| searchable | boolean | true | Whether to render the search field. |
| search / defaultSearch | string | "" | Controlled or initial uncontrolled search query. |
| onSearchChange | (v: string) => void | — | Called when the search query changes. |
| columns | number | 7 | Number of grid columns. |
| iconSize | number | 64 | Icon box size in pixels. |
| labelLines | 1 | 2 | 1 | Maximum number of label lines before truncation. |
| variant | "glass" | "solid" | "glass" | Translucent glass surface or opaque surface. |
| emptyText | ReactNode | "\u6ca1\u6709\u5339\u914d\u7684\u5e94\u7528" | Empty-result message. The built-in Chinese copy means “No matching applications.” |
| onItemClick / onItemContextMenu | (item, event) => void | — | Called for a click or context-menu action. |
When title is not a string, the search field falls back to the built-in Chinese placeholder and accessible label "\u641c\u7d22\u5e94\u7528", meaning “Search applications.” The category group uses "\u5e94\u7528\u5206\u7c7b", meaning “Application categories.”
AppLauncherItem
| Field | Type | Description |
|---|---|---|
| id* | string | number | Unique item key. |
| label* | ReactNode | Application name. |
| icon* | ReactNode | Icon content such as <img>, SVG, or emoji, clipped to a square with 22% corner rounding. |
| category | string | Category key. |
| section | string | Section key. Adjacent items with the same key form a group separated from the next group by a divider. |
| keywords | string[] | Search aliases such as transliterations, English names, or abbreviations. Non-string labels can only match through these keywords. |
| href / target | string | Renders the item as an <a> when href is provided. |
| badge | ReactNode | Badge at the upper-right corner of the icon. |
| disabled | boolean | Prevents activation and removes the item from the tab order. |
matchApp / filterApps / groupSections
These exported pure functions implement keyword matching, category filtering, and contiguous section grouping. Use them when rendering a custom grid with the same behavior.
Example
<AppLauncher
items={apps}
categories={[{ key: "dev", label: "Developer tools" }, { key: "tool", label: "Utilities" }]}
title="Applications"
logo={<Logo />}
actions={<MoreButton />}
className="h-[28rem]"
onItemClick={(app) => router.push(`/apps/${app.id}`)}
/>Usage guidelines
- The `glass` variant needs visible artwork or texture behind it. It uses
bg-surface/70and backdrop blur. On a flat background it reads as a merely translucent panel; usevariant="solid"there. - Search matches Chinese labels by substring, not prefix. Put English, transliterated, and abbreviated aliases in
keywordsinstead of appending them tolabel. - Sections group only adjacent entries and never reorder the input. Array order is the display order, so a “Recently used” group remains first. Reusing a section name in non-adjacent runs intentionally creates separate groups.
- Icon rounding is fixed at 22% to approximate an Apple-style superellipse and does not use
var(--radius). - Set the panel height through
className, for exampleh-[28rem]. The grid scrolls inside that height; without a height constraint, content expands the panel.
Related
Playground
<AppLauncher
items={apps}
categories={categories}
title="Application"
columns={6}
/>