Skip to content

React, Vue and plain HTML

import { useEffect } from 'react';
import { useDuorConfigurator } from '@duordigital/embed/react';
export function KitchenPage() {
const { ref, configurator } = useDuorConfigurator('kitchen-terra', {
origin: 'https://app.duor.digital',
apiKey: import.meta.env.VITE_DUOR_KEY,
locale: 'en',
});
useEffect(() => configurator?.on('lead', (lead) => crm.createLead(lead)), [configurator]);
return <div ref={ref} style={{ height: 640 }} />;
}

The hook cleans up after itself on unmount, and survives a hot reload without leaving a second iframe behind. Mounting two on one page is fine; each keeps its own scene.

<script setup>
import { useDuorConfigurator } from '@duordigital/embed/vue';
const { container } = useDuorConfigurator('kitchen-terra', {
origin: 'https://app.duor.digital',
apiKey: import.meta.env.VITE_DUOR_KEY,
});
</script>
<template><div ref="container" style="height: 640px" /></template>

Mounting it by hand is a trap worth naming: createConfigurator returns a promise, so a cfg assigned from it has no destroy to call on unmount — the widget outlives the component and the next mount adds a second one.

<div id="cfg" style="height:640px"></div>
<script src="https://app.duor.digital/sdk/v1.js"></script>
<script>
DuorEmbed.createConfigurator('kitchen-terra', document.getElementById('cfg'), {
origin: 'https://app.duor.digital',
apiKey: 'pk_live_…',
});
</script>

Two options change the widget’s appearance, and deliberately only two:

{ accent: '#1f6feb', font: 'Inter' }

accent repaints every call to action; font swaps the interface face. The dimensions stay in a monospace face whatever you pass, because 1 and 7 in a proportional face at 11px is a guess, and these are millimetres somebody will cut wood by.

There is no stylesheet override and there will not be one: a widget whose internals a host can restyle is a widget that breaks on the host’s next deploy, and the support ticket comes to us.