LauncherGrid
v1.0.2Updated 19 Oct 2026The tile grid a suite's start page opens on: one tile per application, with an icon, a one-line description, an optional badge and an optional featured tile that spans two columns. Columns step 1 → 2 → the requested count, so the one-column case a phone sees first is the one that is right. A tile with an href is a LINK and one with a handler is a BUTTON, because those are different things to a keyboard and to a middle click; a disabled tile is always a button, since an anchor cannot be disabled and a dead link still navigates on Enter. Carries its own empty and loading states, and loading draws skeleton TILES so the grid keeps its shape.
Preview
Live · 9 stories▸Why this is recommendedlauncherappstilesstart page+5
The default, out-of-the-box LauncherGrid.
- ✓A start page listing the apps someone can open
- ✓An app switcher or launcher for a suite of products
- ✓A home screen of modules with one highlighted
- ✓Show which apps a user does not have access to, and why
- Responsive columns — 1 at the narrowest, 2 from sm, the requested 2–6 from lg; every recipe is a literal class, never lg:grid-cols-${n}, which Tailwind’s scanner cannot see
- A featured tile spans two columns from md up and is an ordinary tile below, where spanning would only break the grid
- Link or button per tile from the props given, so middle-click and open-in-new-tab work where they should
- linkComponent for the host’s router — without it a Next.js app reloads the document on every tile
- A disabled tile carries its reason in the title AND the accessible name, and renders as a disabled button rather than a dead link
- Optional groups — labelled bands for a suite too big for one flat grid, all sharing the column behaviour so tiles stay aligned
- Loading: skeleton tiles on the same column recipe (labelled role="status" + aria-busy), not a centred spinner that lets the page reflow
- Empty: a real state for a tenant with no applications assigned yet, replaceable with emptyState
- Long names truncate and descriptions clamp at two lines, so one verbose app cannot make its row taller than the others
- role="list" / role="listitem" structure with a labelled grid
- i18n: labels prop over DEFAULT_LAUNCHER_GRID_LABELS — the grid name, both empty-state lines, the fallback unavailable reason and the loading announcement
- ✕Navigation inside one app (use Sidebar or AppBar menus)
- ✕A list of records (use List or DataTable)
- ✕Marketing feature cards (use the sections/ features blocks)
- ✕A dense icon-only switcher in a top bar (use the AppBar launcher button)
Install
Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.
Usage
Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.
Props
appsThe flat case. LauncherApp = { id, name, description?, icon?, href?, onSelect?, badge?, disabled?, disabledReason?, featured? }. Ignored when groups is given
—groupsLauncherGroup = { id, label, apps } — labelled bands for a suite too big for one flat grid
—columnsColumns at the widest breakpoint (default 4). The grid steps 1 → 2 → this; a value outside the range is clamped rather than producing a grid with no columns at all
—linkComponentThe host's router link (next/link, react-router Link). Tiles with an href render through it; without it they are plain anchors and every click reloads the document
—loadingDraw skeleton tiles instead of apps. Wins over apps/groups
—loadingCountHow many skeleton tiles (default 6, minimum 1)
—emptyStateReplaces the built-in empty state entirely, for a host that wants its own call to action
—labelsOverride any built-in string. Keys: applications, emptyTitle, emptyHint, unavailable, loadingApplications. English defaults ship verbatim as DEFAULT_LAUNCHER_GRID_LABELS. A tile’s own disabledReason is a prop on the app, not a label, so each one can say something different
—