<!-- Generated by scripts/gen-docs.mts from the JSDoc in registry/. Do not edit; run `pnpm run docs`. -->

# InstallPrompt

A quiet offer to install the web app, shown only where the browser can install it (Chromium on Android and desktops), never while it already runs installed, and not again for two weeks after Not now. Install opens the browser's own install dialog. It renders nothing otherwise, so it can sit in a layout unconditionally. The browser's install event is caught as soon as the module loads, so an offer is never missed because the page mounted late. When Install or Not now takes the card away while it has focus, focus goes back where it was before the card, or to the next control on the page when that is gone.

```tsx
import { InstallPrompt } from "@/components/ouro/install-prompt/install-prompt";
```

## Example

```tsx
<InstallPrompt className="mt-6" />
```

## Use instead when

- Use `useInstallPrompt` instead to offer installing from a control of your own, such as a menu item.

## Props

Type: `InstallPromptProps`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` |  | Layout only: margins, width (`mt-6`, `max-w-md`). Appearance classes warn in development. |
| `description` | `ReactNode` | `"Add it to your home screen or dock and open it like any other app."` | What installing gives the person. |
| `dismissLabel` | `string` | `"Not now"` | The label of the button that puts the offer away. |
| `headingLevel` | `2 \| 3 \| 4 \| 5 \| 6` | `2` | The title's heading level. |
| `installLabel` | `string` | `"Install"` | The label of the button that opens the browser's install dialog. |
| `media` | `ReactNode` |  | A picture across the top, such as the app on a phone. |
| `onInstall` | `((outcome: "accepted" \| "dismissed") => void)` |  | Called with how the person answered the browser's install dialog. |
| `remindAfterDays` | `number` | `14` | Days before offering again after Not now. |
| `storageKey` | `string` | `"ouro-install-dismissed"` | The `localStorage` key that keeps when the person said Not now. |
| `title` | `string` | `"Install the app"` | The offer's title. |

## Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

- `onAccept`: Not a prop. Ouro UI says `onInstall`: `<InstallPrompt onInstall={(outcome) => …} />`.
- `open`: Not a prop. The offer shows itself when the browser can install the app; there is nothing to open.

## `className` is for layout only

Pass margins, width and grid placement (`mt-4`, `w-full`, `col-span-2`). Your classes always win over Ouro UI's: InstallPrompt merges `className` last with `cn` (tailwind-merge), so a class that conflicts with its own replaces it.[^layout-classes]

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (`hover:`, `sm:`) and any `!` are removed, it starts with `bg-`, `text-`, `border-`, `rounded-`, `shadow-`, `font-`, `ring-`, `from-`, `via-`, `to-`, `fill-` or `stroke-`, or is `border`, `rounded`, `shadow` or `ring`, or has a `dark:` variant. Text wrapping, overflow and table layout classes (`text-wrap`, `text-ellipsis`, `border-collapse`) are layout and do not warn.

Alignment classes (`text-center`) warn too: set alignment on a parent element.

[^layout-classes]: test:registry/ouro/components/ouro/install-prompt/install-prompt.test.tsx::InstallPrompt > lets app layout classes win verify:testExists(registry/ouro/components/ouro/install-prompt/install-prompt.test.tsx::InstallPrompt > lets app layout classes win)
