# Menu (React Native)

> Action menus with checkbox and radio items and submenus. An action sheet on phones, a popover on tablets.

GramproKit for React Native · Overlays · Beta · Source: https://kit.gramproindia.com/native/docs/menu

> **Beta:** React Native support is in beta. APIs may change or break between prereleases (npm tag `native`).

_Interactive demo:_ [open the live example](https://kit.gramproindia.com/native/docs/menu). The example's source:

```tsx
import { useState } from "react";
import { Text } from "react-native";
import { Button } from "@/component-lib/button";
import {
  Menu,
  MenuCheckboxItem,
  MenuGroup,
  MenuItem,
  MenuRadioGroup,
  MenuRadioItem,
  MenuSeparator,
  MenuSub,
} from "@/component-lib/menu";
import { useKitTheme } from "@/component-lib/shared/native";
import { Row, Section } from "./layout"; // Section and Row: page layout for the demo

export function MenuDemo() {
  const { tokens } = useKitTheme();
  const [last, setLast] = useState("—");
  const [wrap, setWrap] = useState(true);
  const [density, setDensity] = useState("comfortable");

  const items = (
    <>
      <MenuGroup label="Invoice">
        <MenuItem onSelect={() => setLast("Edit")} shortcut="⌘E">
          Edit
        </MenuItem>
        <MenuItem onSelect={() => setLast("Duplicate")}>Duplicate</MenuItem>
        <MenuSub label="Export as">
          <MenuItem onSelect={() => setLast("PDF")}>PDF</MenuItem>
          <MenuItem onSelect={() => setLast("CSV")}>CSV</MenuItem>
          <MenuSub label="Spreadsheet">
            <MenuItem onSelect={() => setLast("XLSX")}>Excel (.xlsx)</MenuItem>
            <MenuItem disabled>Google Sheets</MenuItem>
          </MenuSub>
        </MenuSub>
        <MenuItem disabled>Send reminder</MenuItem>
      </MenuGroup>
      <MenuSeparator />
      <MenuCheckboxItem checked={wrap} onCheckedChange={setWrap}>
        Wrap long lines
      </MenuCheckboxItem>
      <MenuRadioGroup label="Density" value={density} onValueChange={setDensity}>
        <MenuRadioItem value="compact">Compact</MenuRadioItem>
        <MenuRadioItem value="comfortable">Comfortable</MenuRadioItem>
      </MenuRadioGroup>
      <MenuSeparator />
      <MenuItem destructive onSelect={() => setLast("Delete")}>
        Delete invoice
      </MenuItem>
    </>
  );

  return (
    <Section title="Menu · items, groups, submenus">
      <Row>
        <Menu trigger={<Button variant="outline">Actions (auto)</Button>} label="Invoice actions">
          {items}
        </Menu>
        <Menu trigger={<Button variant="outline">As popover</Button>} presentation="popover">
          {items}
        </Menu>
      </Row>
      <Text style={{ color: tokens.muted }}>
        Last: {last} · wrap {wrap ? "on" : "off"} · {density}
      </Text>
    </Section>
  );
}
```

## Install

```bash
npx @grampro/kit@native add Menu
```

## Usage

```tsx
import { Menu, MenuItem } from "@/component-lib/menu";

<Menu trigger={<Button variant="outline">Actions</Button>}>
  <MenuItem onSelect={duplicate}>Duplicate</MenuItem>
  <MenuItem onSelect={archive}>Archive</MenuItem>
</Menu>
```

## Props

### MenuProps

| Prop | Type | Description |
|---|---|---|
| `trigger` (required) | `ReactElement<TriggerProps>` | The element that opens the menu, cloned with a ref (to measure where it is) and an `onPress`. Any Kit button, or any Pressable, works. |
| `children` (required) | `ReactNode` |   |
| `open` | `boolean` | Controlled open state. |
| `defaultOpen` | `boolean` | Open to begin with, when you are not controlling `open`. |
| `onOpenChange` | `(open: boolean, reason?: MenuCloseReason) => void` | `reason` is `outside` (tap outside or Android back), `trigger`, `select` or `api`, and undefined when opening. |
| `label` | `string` | Names the menu for screen readers. Defaults to the locale text. |
| `side` | `MenuSide` | Default `bottom`. Popover presentation only. |
| `align` | `MenuAlign` | Default `start`. Popover presentation only. |
| `gap` | `number` | Distance from the trigger. Default 4. |
| `closeOnSelect` | `boolean` | Close the menu when an item is chosen. Default true. |
| `presentation` | `"auto" \| "sheet" \| "popover"` | `auto` (default): an action sheet on phones, a popover on tablets. Or force `sheet` or `popover`. |
| `style` | `StyleProp<ViewStyle>` | The list's style. |
| `styles` | `MenuStyles` |   |
| `localeText` | `Partial<MenuLocaleText>` |   |

### MenuItemProps

| Prop | Type | Description |
|---|---|---|
| `onSelect` | `() => void` | Called when the item is chosen. |
| `closeOnSelect` | `boolean` | Keep the menu open after choosing. Defaults to the Menu's `closeOnSelect`. |
| `children` (required) | `ReactNode` |   |
| `icon` | `ReactNode` | Drawn before the label. |
| `shortcut` | `ReactNode` | A hint such as ⌘K, for hardware keyboards. The menu shows it; binding the key is up to you. |
| `disabled` | `boolean` |   |
| `destructive` | `boolean` | Styles it as a destructive action. |
| `style` | `StyleProp<ViewStyle>` |   |

### MenuSubProps

| Prop | Type | Description |
|---|---|---|
| `label` (required) | `ReactNode` | The label of the item that opens the submenu. |
| `children` (required) | `ReactNode` |   |
| `icon` | `ReactNode` | Drawn before the submenu's label. |
| `disabled` | `boolean` |   |
| `style` | `StyleProp<ViewStyle>` |   |

## Native and web

- In a sheet, a submenu replaces the list with a back row.
- Web version: [Menu on the web](https://kit.gramproindia.com/menu).
