---
name: ndl-react
metadata:
  package: '@neo4j-ndl/react'
  version: '4.25.8'
description: >-
  Build and edit UI with the Neo4j Needle React design system
  (@neo4j-ndl/react). Use when adding, composing, or modifying Needle components
  in a React app. Provides per-component props, accessibility requirements, and
  runnable usage examples. Works with any agent that supports skills (Cursor,
  Claude, etc.).
---

[//]: # (This file is auto-generated by the docs:generate-skills script, do not edit it manually)

# Neo4j Needle (@neo4j-ndl/react)

> 📌 Shipped with `@neo4j-ndl/react` v4.25.8. Once installed into your repo (e.g. via
> `npx skills add`) this folder is detached from the package — the installed
> package (TypeScript types in `node_modules/@neo4j-ndl/react`) is the source of
> truth.

Use Neo4j Needle components instead of hand-rolling equivalent UI.

## Setup

- Import the base stylesheet once at the app root (if not already wired):
  `import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';`
- Import components from `@neo4j-ndl/react`; some live under the
  `@neo4j-ndl/react/next` and `@neo4j-ndl/react/ai` subpaths (see the catalog).
- Prefer icons exported from `@neo4j-ndl/react/icons` (e.g.
  `MagnifyingGlassIconOutline`) for design-system parity.

## Rules

- **Never hallucinate props.** Before using any prop — including common-sounding
  ones like `size`, `tone`, or `shadow` — confirm it appears in the component's
  doc here or in the package's TypeScript types. Do not assume APIs from other
  libraries (MUI, Radix, Chakra). If something is unclear, ask the user — or when
  running unattended, prefer the TypeScript types and omit the uncertain prop
  rather than guessing.
- These docs are the primary source for props, accessibility, and examples. The
  package's `.d.ts` type contract is authoritative: if the types disagree with
  these docs, the **types win**. Do not reverse-engineer behavior from the
  compiled implementation source in `node_modules`.
- **Customize via component props first.** Needle components expose typed
  props for spacing, layout, variant, and accessibility — use those before
  reaching for `className` or `htmlAttributes`. The escape-hatch order is:
  1. **Component props** (e.g. `gap="8"`, `variant="danger"`, `size="small"`) —
     always preferred; type-checked and token-constrained.
  2. **`className`** — for utility classes the component doesn't cover (e.g.
     `n-text-neutral-text-weak` or `n-body-small` on a plain element or when
     composing). Verify every class against the stylesheet; see the class-names
     rule below.
  3. **`htmlAttributes`** — for native HTML attributes, `aria-*`, and `data-*`
     the component doesn't expose as props. Valid reasons include:
     - `id` — for `aria-controls` references, portal targets, or form label wiring
     - `aria-*` — when no dedicated prop exists (e.g. `aria-haspopup`, `aria-expanded`
       on a trigger, `aria-label` on a TextInput without a label)
     - `data-*` — custom data attributes for testing or integration
     - Native HTML attributes not surfaced as props (e.g. `type="email"`,
       `type="password"`, `max`/`min`/`step` on a TextInput, `href` on
       Typography as `as="a"`)
     Do **not** use `htmlAttributes` to override behavior the component already
     provides (e.g. setting `role="alertdialog"` on a Dialog — use `variant`
     instead), or for styling — use component props or `className`.
- Compound components are exposed as subcomponents (e.g. `Tooltip.Trigger`,
  `Tooltip.Content`). Follow each component's own doc — don't assume one
  component's compound shape matches another's.
- **Class names are unchecked strings, and the prefix is not fixed.** A wrong
  prop fails the build, but `className="n-justify-between"` compiles and
  silently renders nothing. Which utility classes exist — and whether they carry
  an `n-` prefix at all — depends on the consuming app, so establish that first
  rather than assuming:
  - No Tailwind build: only classes literally present in
    `node_modules/@neo4j-ndl/base/lib/neo4j-ds-styles.css` apply. That means
    the typography classes (`n-body-medium`, `n-title-1`, …) and the
    safelisted color classes (`n-text-*`, `n-bg-*`, `n-border-*`) — no
    layout or spacing utilities such as `n-flex` or `n-gap-token-8`. Grep it
    for the exact class.
  - Tailwind with the published preset and no override: the preset sets
    `prefix: 'n-'`, so `n-flex` works and `flex` does not.
  - Tailwind where the app's own config sets `prefix` (it overrides the preset,
    and `prefix: ''` is common): the theme is Needle's but the classes are
    unprefixed — `flex` and `text-neutral-text-weaker` work, `n-flex` does not.

  Check the app's Tailwind config for `prefix` and follow the surrounding code.
  Tailwind-shaped guesses (`n-justify-between`, `n-items-baseline`) and
  near-misses on real scales (`n-gap-token-1` when the scale is 2/4/6/8/12/16)
  become invisible layout bugs, not errors. `Flex`, `Box` and `Typography` with
  token props sidestep the question entirely — prefer them. For the full token
  list and class verification, see [references/styling.md](references/styling.md).
- **Tokens only.** Never hardcode `hex`, `rgb()`, `oklch()`, or arbitrary
  Tailwind color values in generated code. All colors come from semantic theme
  custom properties (`--theme-color-*`) that resolve to the correct light/dark
  value automatically. See [references/styling.md](references/styling.md) for
  the complete token catalog.
- Follow the accessibility guidance in each component's doc; don't strip built-in
  roles or keyboard behavior.

## Reuse before you create

Needle is a maintained design system. Before writing custom UI, check whether an
existing component already covers the need — you get the maintained component,
not a lookalike you now own.

1. **Scan the component catalog below.** The table lists every shipped
   component with its docs file and import path. If a Needle component covers
   the need, use it instead of hand-rolling an equivalent.
2. **Read the component's doc** (`components/<file>`) before using it — props,
   compound shape, and accessibility behavior differ per component.
3. **Create only when no component fits.** If the catalog doesn't cover it,
   compose existing components (e.g. `Flex` + `Typography` + `Banner.Actions`)
   rather than introducing a new component. Only create a new component when the
   pattern is genuinely novel and no combination of existing primitives works.

### Don't restyle a component into another

Each button variant is a distinct component with its own visual language.
Don't take a `FilledButton` and restyle it to look like an `OutlinedButton` —
use `OutlinedButton` directly. Same for tags (`Tag`, `SelectableTag`,
`DismissibleTag`, `ReadOnlyTag`), and any other variant family.

## How to use this skill

1. Find the component in the catalog below to get its docs file and import path.
2. Read `components/<file>` for that component's props, accessibility
   requirements, and runnable examples; prefer those examples.
3. For styling — token names, utility class lists, prefix rules — consult
   [references/styling.md](references/styling.md). It points at the shipped
   stylesheet so class and token lists can't drift from what the package
   actually ships.

## Components

Import from `@neo4j-ndl/react`.

| Component | Docs | Accessibility | Examples |
|-----------|------|---------------|----------|
| `Accordion` | [components/accordion.md](components/accordion.md) | ✅ | 6 |
| `AnnouncementBanner` | [components/announcement-banner.md](components/announcement-banner.md) | ✅ | 9 |
| `Avatar` | [components/avatar.md](components/avatar.md) | ✅ | 7 |
| `Banner` | [components/banner.md](components/banner.md) | ✅ | 6 |
| `Box` | [components/box.md](components/box.md) |  | 1 |
| `Breadcrumbs` | [components/breadcrumbs.md](components/breadcrumbs.md) | ✅ | 4 |
| `Checkbox` | [components/checkbox.md](components/checkbox.md) | ✅ | 9 |
| `CheckboxGroup` | [components/checkbox-group.md](components/checkbox-group.md) | ✅ | 6 |
| `CleanIconButton` | [components/clean-icon-button.md](components/clean-icon-button.md) | ✅ | 8 |
| `ClipboardButton` | [components/clipboard-button.md](components/clipboard-button.md) |  | 7 |
| `Code` | [components/code.md](components/code.md) | ✅ | 2 |
| `CodeBlock` | [components/code-block.md](components/code-block.md) |  | 8 |
| `ColorPicker` | [components/color-picker.md](components/color-picker.md) |  | 5 |
| `Combobox` | [components/combobox.md](components/combobox.md) | ✅ | 26 |
| `ConditionalWrap` | [components/conditional-wrap.md](components/conditional-wrap.md) |  | 1 |
| `DataGrid` | [components/data-grid.md](components/data-grid.md) |  | 20 |
| `DatePicker` | [components/date-picker.md](components/date-picker.md) | ✅ | 15 |
| `Dialog` | [components/dialog.md](components/dialog.md) | ✅ | 7 |
| `DismissibleTag` | [components/dismissible-tag.md](components/dismissible-tag.md) |  | 4 |
| `Divider` | [components/divider.md](components/divider.md) |  | 3 |
| `Drawer` | [components/drawer.md](components/drawer.md) | ✅ | 8 |
| `DropdownButton` | [components/dropdown-button.md](components/dropdown-button.md) |  | 7 |
| `Dropzone` | [components/dropzone.md](components/dropzone.md) |  | 6 |
| `Fieldset` | [components/fieldset.md](components/fieldset.md) | ✅ | 6 |
| `FilledButton` | [components/filled-button.md](components/filled-button.md) |  | 0 |
| `Flex` | [components/flex.md](components/flex.md) |  | 1 |
| `GraphLabel` | [components/graph-label.md](components/graph-label.md) |  | 7 |
| `IconButton` | [components/icon-button.md](components/icon-button.md) | ✅ | 7 |
| `IconButtonArrayComponent` | [components/icon-button-array.md](components/icon-button-array.md) |  | 5 |
| `InlineEdit` | [components/inline-edit.md](components/inline-edit.md) | ✅ | 9 |
| `Kbd` | [components/kbd.md](components/kbd.md) | ✅ | 2 |
| `LoadingBar` | [components/loading-bar.md](components/loading-bar.md) |  | 2 |
| `LoadingSpinner` | [components/loading-spinner.md](components/loading-spinner.md) | ✅ | 2 |
| `Logo` | [components/logo.md](components/logo.md) |  | 3 |
| `Menu` | [components/menu.md](components/menu.md) | ✅ | 13 |
| `Modal` | [components/modal.md](components/modal.md) | ✅ | 0 |
| `OutlinedButton` | [components/outlined-button.md](components/outlined-button.md) |  | 0 |
| `Popover` | [components/popover.md](components/popover.md) | ✅ | 6 |
| `ProgressBar` | [components/progress-bar.md](components/progress-bar.md) | ✅ | 3 |
| `Radio` | [components/radio.md](components/radio.md) | ✅ | 7 |
| `RadioGroup` | [components/radio-group.md](components/radio-group.md) | ✅ | 8 |
| `ReadOnlyTag` | [components/read-only-tag.md](components/read-only-tag.md) |  | 4 |
| `SegmentedControl` | [components/segmented-control.md](components/segmented-control.md) | ✅ | 5 |
| `Select` | [components/select.md](components/select.md) |  | 14 |
| `SelectIconButton` | [components/select-icon-button.md](components/select-icon-button.md) |  | 4 |
| `SelectableTag` | [components/selectable-tag.md](components/selectable-tag.md) |  | 6 |
| `SideNavigation` | [components/side-navigation.md](components/side-navigation.md) |  | 4 |
| `Skeleton` | [components/skeleton.md](components/skeleton.md) |  | 5 |
| `Slider` | [components/slider.md](components/slider.md) | ✅ | 8 |
| `Spotlight` | [components/spotlight.md](components/spotlight.md) | ✅ | 9 |
| `StatusIndicator` | [components/status-indicator.md](components/status-indicator.md) | ✅ | 1 |
| `StatusLabel` | [components/status-label.md](components/status-label.md) |  | 4 |
| `Switch` | [components/switch.md](components/switch.md) | ✅ | 7 |
| `Tabs` | [components/tabs.md](components/tabs.md) | ✅ | 7 |
| `Tag` | [components/tag.md](components/tag.md) |  | 2 |
| `TextArea` | [components/text-area.md](components/text-area.md) | ✅ | 8 |
| `TextButton` | [components/text-button.md](components/text-button.md) |  | 0 |
| `TextInput` | [components/text-input.md](components/text-input.md) | ✅ | 16 |
| `TextLink` | [components/text-link.md](components/text-link.md) | ✅ | 5 |
| `TimePicker` | [components/time-picker.md](components/time-picker.md) |  | 5 |
| `TimeZonePicker` | [components/timezone-picker.md](components/timezone-picker.md) |  | 10 |
| `Tooltip` | [components/tooltip.md](components/tooltip.md) | ✅ | 13 |
| `TreeView` | [components/tree-view.md](components/tree-view.md) |  | 3 |
| `Typography` | [components/typography.md](components/typography.md) |  | 3 |
| `Wizard` | [components/wizard.md](components/wizard.md) |  | 5 |

## Next components

Import from `@neo4j-ndl/react/next`.

Next-generation components, imported from the `@neo4j-ndl/react/next` subpath. A `next` component may share a name with a stable one (e.g. `TreeView`) but is a **distinct** implementation with its own API — import it from this subpath, and do not mix it with the same-named default component.

| Component | Docs | Accessibility | Examples |
|-----------|------|---------------|----------|
| `TreeView` | [components/next/tree-view.md](components/next/tree-view.md) | ✅ | 11 |

## AI components

Import from `@neo4j-ndl/react/ai`.

Building blocks for AI chat / agent UIs, imported from the `@neo4j-ndl/react/ai` subpath.

| Component | Docs | Accessibility | Examples |
|-----------|------|---------------|----------|
| `CodePreview` | [components/ai/code-preview.md](components/ai/code-preview.md) |  | 4 |
| `FileTag` | [components/ai/file-tag.md](components/ai/file-tag.md) |  | 3 |
| `ImageTag` | [components/ai/image-tag.md](components/ai/image-tag.md) |  | 3 |
| `MoreFilesComponent` | [components/ai/more-files.md](components/ai/more-files.md) |  | 1 |
| `Presence` | [components/ai/presence.md](components/ai/presence.md) |  | 2 |
| `Preview` | [components/ai/preview.md](components/ai/preview.md) | ✅ | 9 |
| `Prompt` | [components/ai/prompt.md](components/ai/prompt.md) |  | 6 |
| `Reasoning` | [components/ai/reasoning.md](components/ai/reasoning.md) |  | 1 |
| `Response` | [components/ai/response.md](components/ai/response.md) |  | 7 |
| `Suggestion` | [components/ai/suggestion.md](components/ai/suggestion.md) |  | 4 |
| `Thinking` | [components/ai/thinking.md](components/ai/thinking.md) |  | 2 |
| `ToolCall` | [components/ai/tool-call.md](components/ai/tool-call.md) |  | 2 |
| `UserBubbleComponent` | [components/ai/user-bubble.md](components/ai/user-bubble.md) |  | 4 |
