Component Preview
Render isolated HTML/CSS components with live style hot-patching — no full re-render needed when the stylesheet changes.
Problem
You're building a component documentation site or design tool. You want to show live component previews in iframes that are isolated from the host page styles, and update the component stylesheet instantly as the user tweaks CSS tokens — without blowing away the component's DOM state.
Solution
ts
import { createSandbox } from '@vielzeug/sandbox';
function createComponentPreview(container: HTMLElement, componentCss: string) {
const sandbox = createSandbox(container, {
namedStyles: {
'component-css': componentCss,
},
});
sandbox.onMessage((msg) => {
if (msg.type === 'resize') {
const iframe = container.querySelector('iframe') as HTMLIFrameElement | null;
if (iframe) iframe.style.height = `${msg.height}px`;
}
if (msg.type === 'error') {
console.error('[preview]', msg.message);
}
});
return {
async render(html: string) {
await sandbox.render(html);
},
// Patch CSS live — no re-render, component state preserved
updateCss(css: string) {
sandbox.updateStyle('component-css', css);
},
[Symbol.dispose]() {
sandbox.dispose();
},
};
}
// Usage
using preview = createComponentPreview(
document.getElementById('preview')!,
`body { margin: 0; padding: 1rem; font-family: system-ui; }`,
);
await preview.render('<button class="btn">Click me</button>');
// Later: user changes a token — patch without re-render
preview.updateCss(`
body { margin: 0; padding: 1rem; font-family: system-ui; }
.btn { background: oklch(0.6 0.2 260); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; }
`);How auto-resize works
The bridge script sets up a ResizeObserver on document.body automatically. When content height changes — on load, after style patches, or after DOM mutations from setState() — a { type: 'resize', height } message fires to the host. No manual wiring is needed in the sandbox content.
Pitfalls
namedStyleskeys must match betweencreateSandboxandupdateStyle— if the key doesn't exist in the initialnamedStyles,updateStylestill patches the baseline but there is no<style id>element in the live document to target.- Re-render replaces the document —
render()is a full document reset. PreferupdateStyle()for CSS-only changes.