npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-3d-mockups

v0.1.1

Published

Interactive, GPU-accelerated 3D device mockups for React. Drop any content onto the screen of a 3D phone and it stays fully live.

Readme

react-3d-mockups

Live React components on procedural 3D devices - no GLB files. Docs, live demos and a gallery of all 51 models →

GPU-accelerated 3D device mockups for React. Put any content on the screen of a 3D device - real DOM, projected onto WebGL glass, so it stays live: text is vector crisp at any angle, videos play, iframes load, React state and effects keep running. Mockups are decorative: you rotate and zoom them, and the hardware masks the screen pixel for pixel (why).

In active development. The API may change over the next few weeks. Breaking changes are listed in the changelog with what to change; pin an exact version (npm install --save-exact react-3d-mockups) if you need it to stay put.

  • Thirty-three devices - the Galaxy S26 line (S26, S26 Ultra), two foldable generations (Z Fold 7, the wide Z Fold 8, Z Fold 8 Ultra, Z Flip 7, Z Flip 8) and Apple's foldable iPhone Duo, the full iPhone 17 family (17, 17 Air, 17 Pro, 17 Pro Max) and the iPhone 18 Pro / Pro Max, MacBook Air 13"/15", MacBook Pro 14"/16" (M5) and the MacBook Neo 13", iPad Pro 13"/11" (M5), iPad Air 13"/11" (M4), iPad (A16), Galaxy Tab S11 / S11 Ultra, the Apple Watch Series 11, Series 12 and Ultra 4 and the Galaxy Watch 8, Watch 9 and Watch Ultra 2 on full wristbands, and a Studio Display-style 27" monitor, all procedurally generated at runtime. No GLB files and nothing to host - importing one mockup costs about 11–56 KB gzipped (the whole library: 126 KB), peers excluded. The phones, foldables, tablets, watches and laptops use a small CSG engine (three-bvh-csg, a regular dependency) to machine their ports and speaker/mic holes into the chassis as real cavities; every other mockup leaves it out.
  • True-to-device screens - each virtual display matches the real device's logical resolution in portrait and landscape (table below), so your layouts and breakpoints behave exactly like on the hardware.
  • Real GPU rendering, only when needed - three.js + react-three-fiber, physically-based materials, studio lighting, soft shadows, clamped DPR. A mockup draws a frame only when something moves (a drag, autoRotate, float, a prop change), and not at all while it is scrolled off screen or its tab is hidden.
  • Any content on screen - pass React components, an <iframe> or a <video> as children. State, effects and media playback keep running, and every surface is masked per-pixel by the hardware in front of it.
  • Composable - use the one-liners <GalaxyMockup> / <IPhoneMockup> / <IPhoneDuoMockup> / <LaptopMockup> / <IPadMockup> / <GalaxyTabMockup> / <AppleWatchMockup> / <GalaxyWatchMockup> / <StudioDisplayMockup>, or drop <Galaxy> / <IPhone> / <Laptop> / <IPad> / <GalaxyTab> / <AppleWatch> / <GalaxyWatch> / <StudioDisplay> into your own react-three-fiber scene.

Install

npm install react-3d-mockups three @react-three/fiber @react-three/drei

React 19 (react-three-fiber 9 and drei 10 both require it), three 0.179 to 0.186, @react-three/fiber 9 and @react-three/drei 10. Those are peer dependencies rather than bundled ones, because each has to exist exactly once in an app - two copies of three mean two different THREE.Mesh classes, so instanceof checks and r3f's element catalogue stop matching.

npm 7+ and pnpm 8+ install peers automatically, so npm install react-3d-mockups alone already pulls all three in. Listing them explicitly still records them in your package.json, which is what you want if you import from three yourself. Yarn does not auto-install peers, so there the full command is required.

Quick start

'use client'

import { GalaxyMockup } from 'react-3d-mockups'

export function Hero() {
  return (
    <div style={{ height: 560 }}>
      <GalaxyMockup autoRotate float>
        <YourApp />
      </GalaxyMockup>
    </div>
  )
}

Drag anywhere - body, background, or the screen itself - to orbit; with the canvas focused, the arrow keys turn it and Home resets it. In Next.js, load it client-side only (dynamic(() => import('./mockup'), { ssr: false })) and give the wrapper a fixed height so the page does not shift when the canvas mounts.

Regions & slots

Bare children always fill a mockup's primary region (a phone's screen, a book's cover). Objects with more printable surfaces expose each one as a compound slot - type AFrameSignMockup. and your editor lists exactly the regions the object has:

<AFrameSignMockup autoRotate>
  <AFrameSignMockup.Front>
    <MenuBoard />
  </AFrameSignMockup.Front>
  <AFrameSignMockup.Back surfaceBackground="#20241f" resolution={640}>
    <HoursBoard />
  </AFrameSignMockup.Back>
</AFrameSignMockup>

A slot takes the same three surface settings a mockup does - surfaceBackground, resolution, surfaceStyle - and overrides the mockup's for that one region. One vocabulary, whichever element you hang it off. Every surface has a name, so content lands where the name says no matter what order you write the slots in:

<BrochureMockup>
  <BrochureMockup.FrontLeft><Cover /></BrochureMockup.FrontLeft>
  <BrochureMockup.FrontCenter><Middle /></BrochureMockup.FrontCenter>
  <BrochureMockup.BackLeft><Map /></BrochureMockup.BackLeft>
</BrochureMockup>

The same slots exist on the raw scene components (<AFrameSign.Front> inside your own r3f scene). Slots must be direct children of their mockup (fragments are fine); region names come from each object's spec in the core.

Components

<GalaxyMockup> / <IPhoneMockup> / <LaptopMockup> / <IPadMockup> / <GalaxyTabMockup> / <AppleWatchMockup> / <GalaxyWatchMockup> / <StudioDisplayMockup> - all-in-one

Every device appearance prop, plus float (idle floating animation) and the staging props from <MockupCanvas>: controls, autoRotate, zoom, fullscreen, shadows, background, camera, frameloop, label, screenAccessibility, className, style. The canvas props marked canvas only below tune the renderer rather than the picture and stay on <MockupCanvas>. Transforms are first-class: position, rotation and scale flow straight through to the device group (<IPhoneMockup rotation={[0, 0.25, 0]}>).

<MockupCanvas> - the stage

| Prop | Type | Default | Description | | --- | --- | --- | --- | | controls | boolean | true | Drag-to-orbit controls | | freeRotation | boolean | false | Allow full 360° vertical rotation (straight over the top); off = classic clamped orbit. Canvas only | | autoRotate | boolean \| number | false | Slowly orbit the camera. true is one revolution a minute; a number multiplies that (autoRotate={2} twice as fast) | | zoom | boolean | false | Scroll/pinch zoom (off so pages don't lose scroll) | | shadows | boolean | true | Soft contact shadow | | shadowY | number | -2.05 | Y of the shadow plane (grounds the device). Canvas only - a mockup derives it from the object's framing | | background | string | - | CSS background of the canvas | | camera | r3f camera | [0, 0.5, 7.4], fov 40 | Camera override. Live: a new position/fov moves the camera there | | dpr | number \| [min, max] | [1, 2] | Device-pixel-ratio clamp. Canvas only | | frameloop | 'demand' \| 'always' \| 'never' | 'demand' | When to draw. 'demand' draws only when something changes; use 'always' for your own useFrame animation in a composed scene | | pauseWhenOffscreen | boolean | true | Stop drawing while the canvas is off screen or its tab is hidden. Canvas only | | time | number | - | Seconds on your own clock (a video's frame / fps): autoRotate and float follow it, so the same time draws the same picture | | delayCapture | (reason) => () => void | - | Hold a video render or screenshot until the frame is complete (renderer started, redraw done, screens placed). Wire it to Remotion's delayRender/continueRender; while set, the canvas never pauses | | gl | r3f gl | { antialias: true, alpha: true, powerPreference: 'default' } | Renderer settings, merged over the defaults. Keep alpha on - screens show through transparent pixels. Canvas only | | onCreated | (state) => void | - | r3f's onCreated, e.g. to read gl.info. Canvas only | | label | string | per model | Accessible name; the canvas is exposed as role="img" ("3D mockup of an iPhone") | | screenAccessibility | 'hidden' \| 'visible' | 'hidden' | Screens are decorative, so by default they are aria-hidden and inert; 'visible' exposes their content |

<Galaxy> - the device

Render inside any r3f <Canvas>. Accepts all group props (position, rotation, scale…).

| Prop | Type | Default | Description | | --- | --- | --- | --- | | children | ReactNode | - | Screen content | | color | string | '#101216' | Back panel, and the whole finish - the frame, buttons and (on the Ultra) the camera rings follow from it; the S26's rings are dark chrome on every finish, as on the hardware. A retail colorway id from GALAXY_COLORWAYS ('icyblue'…) brings that model's measured metal; any other CSS color gets metal derived from it | | surfaceBackground | string | '#000000' | CSS background behind your content | | variant | 's26' \| 's26ultra' | 's26' | Which Galaxy S26-family device (true relative sizes + per-model cameras) | | orientation | 'portrait' \| 'landscape' | 'portrait' | Landscape lays the device sideways and swaps the virtual display | | resolution | number | per variant | Virtual display width in CSS px (see resolution table) | | surfaceStyle | CSSProperties | - | Extra styles for the screen wrapper |

<IPhone> - iPhone 17 family and iPhone 18 Pro

Same API as <Galaxy>, except: variant is '17' | 'air' | 'pro' | 'promax' | '18pro' | '18promax', and resolution defaults to the variant's logical point grid (see resolution table). Camera architecture follows the real devices: two-lens pill (17), ultra-thin single-lens bar (Air), full-width triple-lens plateau with flash + LiDAR (the Pros). The 18 Pros are the 17 Pros' chassis under the generation's narrower Dynamic Island and colour-matched back.

<IPhoneDuo> - iPhone Duo

Apple's foldable, on the same spec shape and pose vocabulary as <Fold>: openAngle (true open onto the landscape 7.6" inner display, false shut onto the 5.4" cover, or degrees for any Flex angle between), orientation, statusBar (iOS's), color (an IPHONE_DUO_COLORWAYS id - 'nightsky', 'starwhite' - or any CSS color). The inner display has no camera hole - the FaceTime camera sits under the glass - so nothing interrupts your layout there.

<Laptop> - MacBook Air, MacBook Pro and MacBook Neo

Same screen API (surfaceBackground, resolution, surfaceStyle), plus openAngle (lid angle, default 110), and resolution defaulting to the variant's scaled desktop (Air 1280×832, Pro 14 1512×982, Neo 1204×753 - desktop breakpoints apply). color sets the aluminum finish - a LAPTOP_COLORWAYS id ('skyblue', 'starlight', 'midnight', the Neo's 'citrus' and 'indigo') or any CSS color. The Neo ('neo13') is the notchless one: its camera sits in the bezel above a square-cornered panel.

Screens are display-only

Content on the glass renders live, but pointer events never reach it: clicks, scrolling and typing all belong to the orbit controls, so a drag anywhere - body, background, or screen - rotates the model.

That is deliberate, and it is what buys the mockup its looks. A screen is real DOM composited into a WebGL scene, and where that DOM sits in the stacking order decides how hardware can hide it. react-3d-mockups always stacks it under the canvas and masks it with the depth buffer, so anything in front of the screen covers it exactly, pixel for pixel: a laptop's keyboard hides the screen's reflection, a proud camera ring stands over a wrap, a bus's mirrors draw over the livery.

Lifting the DOM above the canvas is the only way to make it clickable, and it costs exactly that masking - nothing in the scene can visually cover DOM that sits on top of it. Hiding degrades to an all-or-nothing guess from sample rays, which is wrong in both directions: content shows through hardware that should hide it, and a mostly-visible screen can blank out entirely. Mockups exist to look right, so that trade is not offered.

If you need a genuinely usable embedded app, render it in the page next to the mockup rather than on it.

The screen renders in its own React root, and React context from above the mockup - a theme, i18n, your router, a query client - is bridged into it, so screen content reads the same providers the rest of your page does. It is kept out of the accessibility tree and the tab order by default (screenAccessibility).

Exporting images and video

The screen is DOM composited by the browser, not pixels in the WebGL canvas, so canvas.toDataURL() gives you devices with empty screens. Screenshot the element in a real browser instead (Playwright's locator.screenshot() captures both layers), or use Remotion for video. Recipes: Exporting images and video.

Virtual screen resolutions

Every variant's screen defaults to the real device's logical resolution (CSS px):

| Device | variant | Portrait | Landscape | Basis | | --- | --- | --- | --- | --- | | Galaxy S26 | s26 | 360×780 | 780×360 | 2340×1080 panel at ⅓ (3x) | | Galaxy S26 Ultra | s26ultra | 384×833 | 833×384 | One UI default FHD+ render @ 450 dpi | | Galaxy Z Fold 7 (open / folded) | fold7 | 820×910 / 360×835 | swapped | inner 2184×1968, cover 2520×1080 | | Galaxy Z Fold 8 (open / folded) | fold8 | 1020×770 / 480×758 | swapped | inner 2448×1848 (natively landscape), cover 1248×1972 | | Galaxy Z Fold 8 Ultra (open / folded) | fold8ultra | 820×910 / 360×835 | swapped | inner 2504×2256 on the Fold 7 grid, cover 2520×1080 | | Galaxy Z Flip 7 (open / folded) | flip7 | 360×838 / 316×349 | swapped | main 2520×1080, cover 948×1048 | | Galaxy Z Flip 8 (open / folded) | flip8 | 360×840 / 316×349 | swapped | main 2520×1080 at 6.9", cover 948×1048 | | iPhone 17 | 17 | 402×874 | 874×402 | 2622×1206 @ 3x point grid | | iPhone 17 Air | air | 420×912 | 912×420 | 2736×1260 @ 3x point grid | | iPhone 17 Pro | pro | 402×874 | 874×402 | 2622×1206 @ 3x point grid | | iPhone 17 Pro Max | promax | 440×956 | 956×440 | 2868×1320 @ 3x point grid | | iPhone 18 Pro | 18pro | 402×874 | 874×402 | 2622×1206 @ 3x point grid | | iPhone 18 Pro Max | 18promax | 440×956 | 956×440 | 2868×1320 @ 3x point grid | | iPhone Duo (open / folded) | duo | 890×626 / 466×678 | swapped | inner 2670×1878 (natively landscape) and cover 1398×2034 @ 3x point grid | | MacBook Air 13" (M5) | air13 | - | 1280×832 | 2560×1664 @ 2x default scaled | | MacBook Air 15" (M5) | air15 | - | 1440×932 | 2880×1864 @ 2x default scaled | | MacBook Pro 14" (M5) | pro14 | - | 1512×982 | 3024×1964 @ 2x default scaled | | MacBook Pro 16" (M5) | pro16 | - | 1728×1117 | 3456×2234 @ 2x default scaled | | MacBook Neo 13" | neo13 | - | 1204×753 | 2408×1506 @ 2x default scaled | | iPad Pro 13" (M5) | ipadpro13 | 1032×1376 | 1376×1032 | 2752×2064 @ 2x point grid | | iPad Pro 11" (M5) | ipadpro11 | 834×1210 | 1210×834 | 2420×1668 @ 2x point grid | | iPad Air 13" (M4) | ipadair13 | 1024×1366 | 1366×1024 | 2732×2048 @ 2x point grid | | iPad Air 11" (M4) | ipadair11 | 820×1180 | 1180×820 | 2360×1640 @ 2x point grid | | iPad (A16) | ipad11 | 820×1180 | 1180×820 | 2360×1640 @ 2x point grid | | Galaxy Tab S11 | tabs11 | 800×1280 | 1280×800 | 2560×1600 panel at ½ (xhdpi) | | Galaxy Tab S11 Ultra | tabs11ultra | 924×1480 | 1480×924 | 2960×1848 panel at ½ (xhdpi) | | Apple Watch Series 11 46mm | series11 | 208×248 | - | 416×496 @ 2x point grid | | Apple Watch Series 12 46mm | series12 | 208×248 | - | 416×496 @ 2x point grid | | Apple Watch Ultra 4 49mm | ultra4 | 211×257 | - | 422×514 @ 2x point grid | | Galaxy Watch 8 44mm | watch8 | 240×240 | - | 480×480 round panel at ½ | | Galaxy Watch 9 44mm | watch9 | 240×240 | - | 480×480 round panel at ½ | | Galaxy Watch Ultra 2 47mm | watchultra2 | 249×249 | - | 498×498 round panel at ½ | | Studio Display 27" | - | - | 2560×1440 | 5120×2880 @ 2x point grid |

<IPad> / <GalaxyTab> - the iPad lineup / Galaxy Tab S11 family

Shares the phones' screen/interaction API plus orientation, with a variant prop ('ipadpro13' | 'ipadpro11' | 'ipadair13' | 'ipadair11' | 'ipad11' | 'tabs11' | 'tabs11ultra'). Fully procedural and per-variant accurate: the Pro's camera pod (wide lens, LiDAR, flash) and Pencil charging window, the Air's and standard iPad's bare single lens with the Touch ID top button, back or edge Smart Connector dots and speaker drill rows; protruding camera rings, quad speaker slots, gold pogo contacts and (on the Ultra) the U-shaped display notch on the Galaxy Tabs; brand marks as real vector geometry (Apple glyph, edge-aligned SAMSUNG wordmark) and model wordmarks on the backs; landscape-edge front cameras, USB-C and machined edge buttons on all.

<AppleWatch> / <GalaxyWatch> - smartwatches · <StudioDisplay> - Studio Display-style

Both watches add bandColor and skip orientation. Every device draws its front camera unconditionally - a punch hole, Dynamic Island or notch is hardware, and it obstructs your layout here exactly as it would on the real panel. <AppleWatch> is the Apple Watch family ('series11' | 'series12' | 'ultra4'): the Series' squircle case with the knurled Digital Crown, flush side button and sensor back, worn on the seamless Solo Loop - which has no closure, so it takes no bandOpen - and the Ultra 4's 49 mm titanium case with its raised lip, crown guard and orange Action button, on its ridged, buckled Ocean Band. <GalaxyWatch> is the Galaxy Watch family ('watch8' | 'watch9' | 'watchultra2'): cushion case, round display on its dial puck, flat keys (the 47 mm titanium Ultra 2 adds its orange Quick Button), BioActive puck, worn on a buckled two-strap band that bandOpen lays out flat. The monitor puts the 2026 Studio Display's 27" 5K panel on its tilt stand - uniform bezel, centered camera, the tight rear 2× Thunderbolt 5 + 2× USB-C slot cluster, the captive power cord's circular recess framed by the stand's cable hole and, faithfully, no power button.

Renderer-agnostic device specs (GALAXY_VARIANTS, IPHONE_VARIANTS, IPHONE_DUO_VARIANTS, IPAD_VARIANTS, APPLE_WATCH_VARIANTS, GALAXY_WATCH_VARIANTS, PHONE, IPHONE, LAPTOP… plus each object's region registry and stage framing) are available from the react-3d-mockups/core subpath. It carries no 'use client' directive, so a server component can import a spec for layout math.

Architecture

react-3d-mockups is one package in two layers. All device/object specs, region registries, stage framing, geometry math and shared screen/stage behaviors live in a renderer-agnostic core that depends on three and never on React; the components are the layer that renders it through react-three-fiber.

The main entry re-exports a curated slice of the core (variants, colorways, size types); the full core surface is available from react-3d-mockups/core, which carries no 'use client' directive so a server component can import a spec for layout math. See ARCHITECTURE.md for the layering rule.

Docs & demos

Full documentation and live demos: area.is/react-3d-mockups. Source: github.com/area-is/react-3d-mockups

License

MIT © Ye Joo Park