# Browser frame (goodtabs UI)

A browser window around any content, with tabs and an address field.

## Install

```bash
npx shadcn@latest add https://ui.goodtabs.co/r/browser-frame.json
```

Or, with the @goodtabs namespace configured in components.json: `npx shadcn@latest add @goodtabs/browser-frame`

## Usage

```tsx
import { BrowserFrame } from "@/components/ui/browser-frame"

<BrowserFrame url="ui.goodtabs.co" tabs={[{ label: "goodtabs", active: true }, { label: "Docs" }]}>
  <img src="/screenshot.png" alt="The components page" />
</BrowserFrame>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| url | `string` |  | Shown in the address field. Omit to hide the field. |
| tabs | `{ label: string; active?: boolean; icon?: ReactNode }[]` |  | Tabs across the top. The active one is raised. |
| variant | `"window" | "tab"` | `"window"` | window draws the full chrome; tab draws only tabs and address. |
| secure | `boolean` | `true` | Show a lock before the url. |
| shadow | `boolean` | `false` | Lift the frame with shadow-md. |
| maxWidth | `string` |  | Constrain the frame, for example "640px". |
| onSelect | `(index: number) => void` |  | Makes the tabs buttons and reports the clicked index. |

## When to use

- Around a screenshot or a live demo on a landing page
- In a hero as the visual below the title
- To show a site in context in docs or a changelog

## When not to use

- As real navigation; the tabs are decorative unless onSelect is passed
- Around app UI that is not a web page, such as a mobile screen

## Notes for agents

- Server component by default; pass onSelect from a client component to make the tabs interactive
- The body has no padding, so children control their own; images fill edge to edge
- Window dots use bg-muted-foreground/30, never traffic light colours, so the frame stays monochrome
- Pair with the hero block: put the frame in the hero's children

Docs: https://ui.goodtabs.co/components/browser-frame