Phone frame
A generic phone bezel drawn in hairlines, around a screenshot or live UI.
npx shadcn@latest add https://ui.goodtabs.co/r/phone-frame.json
With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/phone-frame
Inspiration
The tabs worth keeping open.
A hand-picked library for people who build on the web.
A small mobile screen built from live markup.
Screenshot

An image fills the screen edge to edge.
Pair


Two frames side by side, the second offset down.
Usage
import { PhoneFrame } from "@/components/ui/phone-frame"
<PhoneFrame width="240px">
<img src="/app.png" alt="The home screen" className="size-full object-cover" />
</PhoneFrame>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| width | string | "260px" | Maximum width of the whole bezel. The screen keeps a 9:19 ratio. |
Source
phone-frame/phone-frame.tsx
import * as React from "react";
import { cn } from "@/lib/utils";
type PhoneFrameProps = React.ComponentProps<"div"> & {
/** Width of the whole bezel, for example "260px". The screen keeps a 9:19 ratio. */
width?: string;
};
/**
* A generic phone bezel drawn in hairlines. Not modelled on any real device.
* Children fill the screen; the screen clips them to its rounded corners.
*/
export function PhoneFrame({ width = "260px", className, style, children, ...props }: PhoneFrameProps) {
return (
<div
className={cn("w-full rounded-[2.25rem] border bg-muted p-2", className)}
style={{ maxWidth: width, ...style }}
{...props}
>
<div className="relative aspect-[9/19] overflow-hidden rounded-[1.75rem] border bg-background">
<div
aria-hidden="true"
className="absolute top-2 left-1/2 z-10 h-4 w-1/4 -translate-x-1/2 rounded-full border bg-muted"
/>
<div className="absolute inset-0">{children}</div>
</div>
</div>
);
}
For AI agents
Hand your agent this page, the markdown at /components/phone-frame/prompt.md, or the Copy AI prompt button above.
When to use
- Showing a mobile screen on a landing page or in docs
- Beside a browser frame to show the same product on two sizes
When not to use
- When you need a specific device; this is deliberately generic
- Scrolling content that must stay inside; the screen clips without scrolling
Agent notes
- Server component; children fill an absolute inset-0 screen, so images want size-full object-cover
- The notch is a hairline pill, not a real cutout, and sits above children
- Colours are theme tokens only, so the frame follows light and dark mode
The maths
- The screen is 9 : 19, a ratio of 0.474, close to a modern phone's 9 : 19.5.
- Radii nest: the outer corner is 36 = inner 28 + bezel 8, so the gap stays even around the curve.