Skip to content
DevelopersConfigurators (Beta)

Add a configurator to your website ​

The four ways to put a published configurator on a shop page, with a code sample each and when to use it.

This page is for the developer of a shop website. It shows the four ways to put a published configurator on a website, from the simplest to the most flexible.

WayWhat you getCodeUse it when
Share linkiJewel's page of the configuratorNoneYou send the configurator by email or chat, or link to it from a button
iframe embediJewel's 3D view and panel inside your pageOne HTML tagYou want the configurator on a product page, as it is
Viewer API and postMessage bridgeiJewel's viewer, driven by your pageSome JavaScriptYou keep iJewel's view, and your page reads the choices, the price and the cart
Headless SDKYour own controls, price and cart; iJewel's 3D view if you want itJavaScriptYou build your own interface

Before you start ​

  • The configurator must be published (Back in the editor) and public (Embed → Who can open it → Anyone with the link). Refer to Add it to your website.
  • The Drive's Embed dialog has the code of each way filled in for one configurator. Its Send to your developer way gives a developer page, <drive address>/files/<folder id>/integrate, with the code, a live test and the cart checks. It needs no sign-in.

The origin of the iframe

The code starts with https://ijewel3d.com/drive/, or with https://ijewel3d.com/<name>/ in a business workspace. Both have the origin https://ijewel3d.com. Code copied on beta.ijewel3d.com before the release has the origin https://beta.ijewel3d.com; it still works, with that origin in the checks below. Refer to About the beta.

The Link of the Embed dialog opens the configurator on its own page. The customer needs no account.

html
<a href="https://ijewel3d.com/drive/files/<folder id>/view">Design your ring</a>

2. The iframe embed ​

The Embed our configurator way of the Embed dialog gives this code under Paste the configurator on your product page:

html
<iframe src="https://ijewel3d.com/drive/files/<folder id>/embedded" width="100%" height="720" style="border:0"
  allow="xr-spatial-tracking; fullscreen" title="Engagement ring"></iframe>
  • height: set the height that fits your page. The width follows its container.
  • allow: keep xr-spatial-tracking and fullscreen. They let the viewer use the full screen and the device's spatial features.
  • title: the name that screen readers read.

To hear Add to cart from the iframe, paste this once on the same page. It is the second block of the Embed our configurator way. Use the origin of the iframe's address.

html
<script>
  // iJewel configurator: the shopper clicked Add to cart.
  window.addEventListener('message', (event) => {
    if (event.origin !== 'https://ijewel3d.com') return
    const message = event.data
    if (message?.source !== 'ijewel' || message.event !== 'cart:requested') return
    const design = message.data
    // Add the design to your cart here. Keep design.state with the order: it opens the same design again.
    console.log('Add to cart', design.title, design.labels, design.price, design.state)
  })
</script>

The price shows in the iframe with no more setup: the page in the iframe is on the Drive's own address. After a publish, the iframe can show the older version for up to 5 minutes.

3. The viewer API and the postMessage bridge ​

The iJewel viewer gives your page an API for the configurator that it shows.

  • The viewer on your page (a script, not an iframe): the API is window.ijewel.ring.
  • The viewer in an iframe (the embed above): your page sends messages to the iframe, and the iframe answers and sends events. This is the postMessage bridge.

On the same page: window.ijewel.ring ​

window.ijewel.ring is there when the viewer has loaded a configurator. The ready event says when it can answer.

js
const ring = window.ijewel.ring
ring.on('ready', info => console.log('configurator', info.configuratorId, 'revision', info.revision))
ring.on('selection:changed', ({ selection, moved }) => console.log(selection, moved))
ring.on('price:changed', answer => console.log('price', answer))
ring.on('cart:requested', line => myCart.add(line))   // your cart

for (const axis of ring.getAxes()) console.log(axis.label, axis.options.map(o => `${o.label} (${o.state})`))
await ring.select('shape', 'oval')
const price = await ring.getPrice()                    // { total, currency, minorUnits, … } in minor units
const line = ring.getConfiguration()                   // the cart line
const picture = await ring.getImage()                  // a PNG Blob of the ring as it is now
CallWhat it does
getInfo()The configurator, its revision and what it can do
getAxes(), getOptions(axis), getSelection()The choices, their states and the current picks
select(axis, option, { mode }), selectMany(picks), reset()Change the configuration. The 3D view follows
getState(), applyState(state)The state string that opens the same ring again
getPrice()A promise of the price answer
getConfiguration(), requestCart()The cart line; requestCart() prices it and fires cart:requested
getImage(mimeType)A picture of the ring as it is now
on(event, handler)Listen. Returns the function that stops it

The events are ready, selection:changed, price:changed, validation:warning, state:migrated, cart:requested, error, load:started, load:complete and session:closed. The names and the data are the same as in the Headless SDK.

When the customer clicks Add to cart in the viewer's own panel, the window also gets the event ijewel-ring-add-to-cart:

js
window.addEventListener('ijewel-ring-add-to-cart', event => {
  const { configuration } = event.detail   // the cart line
  myCart.add(configuration)
})

In an iframe: the postMessage bridge ​

The viewer in the iframe answers the page that embeds it, and only that page. It sends its events to that page's origin.

The simple way: attachViewer of the SDK. It talks to the iframe for you, and keeps a session of your page in step with the iframe in both directions.

html
<iframe id="viewer" src="https://ijewel3d.com/drive/files/<folder id>/embedded" width="100%" height="600"
  style="border:0" allow="xr-spatial-tracking; fullscreen"></iframe>
<script src="https://releases.ijewel3d.com/libs/configurator-sdk/0.1.0-dev.2/configurator-sdk.iife.js"></script>
<script>
(async () => {
  const { open, attachViewer } = window.IjewelConfigurator
  const session = await open({ drive: 'drive', id: '<folder id>' })
  const iframe = document.getElementById('viewer')
  const detach = attachViewer(session, iframe, {
    origin: new URL(iframe.src).origin,           // the exact origin of the iframe, never '*'
    onError: error => console.warn('viewer:', error.code, error.message),
  })
  session.on('selection:changed', ({ selection }) => console.log(selection))
})()
</script>

The messages, if you write them yourself. A call is a message to the iframe with source: 'ijewel', v: 2, an id of your choice, a method and its params. The answer comes back with the same id, and a result or an error with a code.

js
const iframe = document.getElementById('viewer')
const viewerOrigin = new URL(iframe.src).origin

let nextId = 1
const call = (method, params) =>
  iframe.contentWindow.postMessage({ source: 'ijewel', v: 2, id: nextId++, method, params }, viewerOrigin)

window.addEventListener('message', event => {
  if (event.origin !== viewerOrigin || event.data?.source !== 'ijewel') return
  const message = event.data
  if (message.event === 'ready') call('select', { axis: 'shape', option: 'oval' })   // send calls only after `ready`
  else if (message.event) console.log('event', message.event, message.data)           // selection:changed, …
  else if (message.error) console.warn('call', message.id, message.error.code)
  else console.log('answer', message.id, message.result)
})

A call sent before ready is lost: the iframe still holds its first, empty page. Add the listener before the iframe loads, as above.

The methods are the calls of the table above: getInfo, getAxes, getOptions ({ axis }), getSelection, select ({ axis, option, mode }), selectMany ({ selection, mode }), reset, getState, applyState ({ state }), getConfiguration, describe, resolve, getPrice, requestCart and getImage ({ mimeType }).

  • The viewer answers only the origin of the page that embeds it. A message from another origin gets no answer.
  • The viewer learns that origin from the browser (location.ancestorOrigins, else the referrer). Do not set referrerpolicy="no-referrer" on the iframe: a browser with no ancestorOrigins then gives the viewer no origin, and the bridge stays off.
  • An unknown method gives E_UNKNOWN_METHOD. A method that the page may not call gives E_SCOPE.
  • session:closed says that the configurator closed. Wait for the next ready before you send calls again.

4. The headless SDK ​

SDK version

These pages describe the configurator SDK 0.1.0-dev.2, a preview whose API can still change. The newest version is in latest-version.txt. Refer to Versioning.

The SDK gives your page a session of the published configurator. Your page draws the choices, shows its own price or iJewel's, and puts the configuration in its own cart. The SDK can also mount iJewel's 3D view in an element of your page.

html
<script src="https://releases.ijewel3d.com/libs/configurator-sdk/0.1.0-dev.2/configurator-sdk.iife.js"></script>
<script>
(async () => {
  const { open, mountViewer } = window.IjewelConfigurator
  const session = await open({ drive: 'drive', id: '<folder id>' })
  for (const axis of session.axes()) console.log(axis.label, axis.options.map(o => o.label))
  await session.select('shape', 'oval')
  console.log(await session.price())
  mountViewer(document.getElementById('ring-3d'), session, { camera: 'fit' })
  myCart.add(session.configuration())                  // your cart
})()
</script>

The full guide: Headless SDK. Before you charge, check the cart line on your server: Cart and Server.