iPhone

A device frame drawn from the real measurements, with a screen you put anything in.

Loading preview

Installation

npx shadcn@latest add @loomui/iphone

Usage

import { IPhone } from "@/components/ui/iphone"
<IPhone />

On its own it is a blank device with a grey screen. Fill it with a screenshot:

<IPhone src="/screenshot.png" alt="The app's home screen" />

Or with live markup, which is the whole reason to build the frame out of elements instead of using a picture of one.

<IPhone width={280}>
  <AppShell />
</IPhone>

Anything inside is clipped to the screen's radius and sits under the island.

One number, and it shrinks

<IPhone width={360} />

width is the only measurement you set, and it is a maximum rather than a fixed size. The frame fills the space it is given and stops there, so it never pushes a narrow screen sideways.

Everything else is a real device measurement expressed as a share of the frame's own width, in container units. The rim, both corner radii, the island, the four buttons. That keeps the proportions exact at any width. A percentage border-radius would go elliptical on a box this tall. A scale factor worked out in JavaScript breaks the moment the frame has to shrink.

The rim is a gradient rather than a flat fill. A solid band the same colour all the way round reads as a border. A gradient reads as metal catching light on one side.

<IPhone island={false} buttons={false} />

Turn either off for a cleaner shape, useful when the frame is small enough that the detail becomes noise.

The component is not client-side. There is no state and no listener, so it renders on the server and costs nothing on the client.

Props

PropTypeDefaultDescription
widthnumber300Largest width the device is drawn at.
srcstringnoneScreenshot to fill the screen with.
altstring""Description of the screenshot.
islandbooleantrueDraw the island at the top of the screen.
buttonsbooleantrueDraw the buttons down the sides.
classNamestringnoneMerged onto the frame.

Any other <div> prop is forwarded. children fills the screen when there is no src.

Accessibility

The rim, the island and the buttons are all aria-hidden. They are a picture of a phone, not content. Give src a real alt describing what is on the screen, or leave it empty when the screenshot is decoration beside copy that already says the same thing.