3D assets

Show interactive 3D product models, with optional augmented reality, using the framework-agnostic rx-model-viewer web component.

Storage can hold 3D product models alongside your images, and the <rx-model-viewer> web component renders them in the browser. Drop one tag into any page and customers can orbit, zoom, and (on supported devices) place the model in their room with augmented reality. It is built on three.js and Google's <model-viewer>, both self-hosted in the package, so it makes no third-party requests at runtime.

The component is a standard custom element, so it works in plain HTML and in Vue, React, Nuxt, or Svelte without a framework wrapper.

Install

Bash
npm install @revenexx/storage-model-viewer

Importing the package registers the <rx-model-viewer> element automatically:

JavaScript
import '@revenexx/storage-model-viewer'

You can also load it from a CDN with a module script tag, no build step required:

HTML
<script type="module" src="https://unpkg.com/@revenexx/storage-model-viewer"></script>

Basic usage

Point src at the model and poster at a preview image. Both can come straight from Storage: a 3D asset exposes its mesh, preview, and AR derivatives through query parameters on the delivery URL.

HTML
<div style="height: 70vh">
  <rx-model-viewer
    src="https://storage.revenexx.com/<TENANT_SLUG>/models/flange.glb?d=model"
    poster="https://storage.revenexx.com/<TENANT_SLUG>/models/flange.glb?d=preview"
    auto-rotate
  ></rx-model-viewer>
</div>

Give the element a height

<rx-model-viewer> fills its container (width: 100%; height: 100%). The parent must have a definite height, like height: 70vh or a fixed pixel value. Inside a bare flex item with no height, the canvas collapses to nothing and you see only the background.

Attributes

AttributeDefaultDescription
srcURL of the .glb mesh to render. Required.
posterImage shown before and while the model loads.
enginethreeRendering engine: three (the default three.js renderer) or model-viewer (Google's element, required for AR).
auto-rotateoffSlowly rotate the model.
backgroundtransparentCSS color for the canvas.
camera-orbitInitial camera orbit, for example "45deg 65deg auto".
aroffShow the AR button. Only honored by the model-viewer engine.
ios-srcUSDZ URL for iOS AR.

Attributes are reactive: change one at runtime and the viewer re-renders. Drag-to-rotate, scroll-to-zoom, and image-based lighting are built into both engines.

Supported formats

  • .glb is the model format the viewer renders, supplied through src.
  • USDZ is used only for iOS augmented reality, supplied through ios-src.

A Storage 3D asset gives you both from one source file: ?d=model returns the .glb, ?d=preview returns a preview image (which accepts the same image transform parameters), and ?d=usdz returns the USDZ for iOS.

Augmented reality

AR is opt-in and runs through the model-viewer engine. Set engine="model-viewer", add the ar attribute, and provide ios-src for iOS:

HTML
<div style="height: 70vh">
  <rx-model-viewer
    engine="model-viewer"
    ar
    src="https://storage.revenexx.com/<TENANT_SLUG>/models/chair.glb?d=model"
    ios-src="https://storage.revenexx.com/<TENANT_SLUG>/models/chair.glb?d=usdz"
    poster="https://storage.revenexx.com/<TENANT_SLUG>/models/chair.glb?d=preview"
    auto-rotate
  ></rx-model-viewer>
</div>

Platform behavior:

  • Android places the .glb from src. Export models at real-world (metre) scale so they appear at the right size.
  • iOS uses Quick Look, which requires the USDZ from ios-src. Without ios-src, there is no AR button on iOS.

Framework notes

Vue and Nuxt

Tell the compiler rx-model-viewer is a custom element so it doesn't try to resolve it as a Vue component. In Nuxt:

TypeScript
export default defineNuxtConfig({
  vue: {
    compilerOptions: {
      isCustomElement: (tag) => tag === 'rx-model-viewer' || tag === 'model-viewer',
    },
  },
})

Render the element inside <ClientOnly>, and import the package from a client plugin. If you use the AR engine in Nuxt, also add a top-level import '@google/model-viewer' in that client plugin and add @google/model-viewer to build.transpile, because Vite's scanner does not follow the engine's lazy import on its own.

React

Import the package once, then use the tag directly:

JSX
import '@revenexx/storage-model-viewer'

export function Model() {
  return (
    <div style={{ height: '70vh' }}>
      {/* @ts-expect-error custom element */}
      <rx-model-viewer
        src="https://storage.revenexx.com/<TENANT_SLUG>/models/flange.glb?d=model"
        poster="https://storage.revenexx.com/<TENANT_SLUG>/models/flange.glb?d=preview"
        auto-rotate
      />
    </div>
  )
}

Accessibility note

<rx-model-viewer> does not expose an alt attribute of its own. Provide a text alternative or description near the viewer for users who can't see the 3D content.

What's next

Was this page helpful?