@meetreeve/preview-embed
v2.4.0
Published
Remotion preview editor + UMD preview surface. Substrate (auth, theme, brand, postMessage, surfaces) moved to @meetreeve/embed — see DEV-791.
Readme
@meetreeve/preview-embed
Embed a Reeve composition preview in any site. React component + vanilla <script> drop-in.
Looking for the substrate SDK?
Previously this package was named @meetreeve/embed (1.0–1.3.1). In 2.0.0
(DEV-791) it was split:
| Concern | Package | Repo |
|---|---|---|
| Remotion preview editor (PreviewFrame, useReevePreview, ReevePreviewEditor, UMD bundle, schemas, presets) | @meetreeve/preview-embed | MindFortressInc/reeve-remotion/packages/preview-embed/ |
| Embed tokens (mintEmbedToken, mintAnonEmbedToken), surface components (<ReeveSurface>, <ReeveCommsAdmin>), HostAppBrand registry, postMessage protocol primitives | @meetreeve/embed | MindFortressInc/reeve-embed |
If you only need the preview editor, you're in the right place. If you need
auth tokens, brand chrome, or iframe surface components, install @meetreeve/embed.
The @meetreeve/[email protected]+/react module still ships <ReeveSurface> and
<ReeveCommsAdmin>, but calling PreviewFrame / useReevePreview from there
throws — they live here now.
Install (React)
pnpm add @meetreeve/preview-embed
# or: npm install @meetreeve/preview-embedimport { PreviewFrame } from '@meetreeve/preview-embed/react';
<PreviewFrame
presetId="imessage-conversation"
props={{
contactName: 'Alex',
messages: [{ sender: 'them', text: 'hey!' }],
format: 'portrait',
statusBarTime: '9:41',
}}
/><PreviewFrame> reads aspect ratio from /presets automatically and sizes the wrapper accordingly. The iframe loads from https://videoapi.meetreeve.com by default; override with host prop.
Hook (lower-level)
import { useReevePreview } from '@meetreeve/preview-embed/react';
function MyPreview({ conversation }) {
const { iframeRef, ready, error } = useReevePreview({
presetId: 'imessage-conversation',
props: conversation,
});
return <iframe ref={iframeRef} src="https://videoapi.meetreeve.com/preview/imessage-conversation" sandbox="allow-scripts allow-same-origin" />;
}Install (vanilla — for non-React sites)
<script src="https://videoapi.meetreeve.com/embed/v1/reeve-preview.umd.js"></script>
<div id="my-preview" style="width: 360px"></div>
<script>
const handle = ReevePreview.mount('#my-preview', {
presetId: 'imessage-conversation',
props: { /* ... */ },
});
// Update later
// handle.update({ props: newProps });
// handle.unmount();
</script>The UMD bundle has zero runtime dependencies — drop it on any HTML page, including WordPress, Webflow, plain static sites.
Available presets
Fetch the full list:
import { fetchPresets } from '@meetreeve/preview-embed';
const { presets } = await fetchPresets();Returns 28 compositions: ads, organic social, site content, messaging (iMessage, Slack), data viz, branding, utility, 3D scenes.
postMessage protocol
The component handles this for you. If you want to wire raw iframes:
| Direction | Type | Payload |
|----------------|---------|-----------------------------|
| iframe→parent | ready | {} |
| parent→iframe | state | { payload: <props> } |
| iframe→parent | error | { code, message } |
All messages have envelope { source: 'reeve-preview', v: 1, type: ... }.
The protocol primitives (isReevePreviewMessage, makeStateMessage, etc.)
live in @meetreeve/embed as of 2.0.0 — this package re-exports them from
its root for convenience.
Browser support
Modern evergreen browsers. The UMD bundle requires Promise, fetch, URL, MessageEvent — IE11 not supported.
Authoring forms
Beyond rendering a preview, the SDK provides a headless form hook plus lifted editor templates so consumers can build authoring UIs without rebuilding from scratch.
Headless hook
import { useReevePresetForm, PreviewFrame } from '@meetreeve/preview-embed/react';
function MyAuthoringPage() {
const form = useReevePresetForm('imessage-conversation');
return (
<div className="grid grid-cols-2 gap-4">
{/* Build your own UI from form.values + form.setValues / form.set */}
<input
value={form.values.contactName ?? ''}
onChange={(e) => form.set('contactName', e.target.value)}
/>
<PreviewFrame presetId="imessage-conversation" props={form.values} />
</div>
);
}The hook returns:
values— typed, schema-defaulted, current form statesetValues(next)— replace the whole objectset(path, value)— dot-path setter ('messages.0.text'works)errors—Record<dotPath, message>from Zod, empty when validisValid— booleanreset()— back to defaultsgetFieldProps(path)—{ value, onChange, error, name }for spreading onto inputsgetArrayHelpers(path)—{ items, append, remove, move, replace }for arrays
Authoring with your own UI (all 28 presets)
useReevePresetForm works for any preset ID. Pair it with getFieldProps and
getArrayHelpers to wire your design system's inputs in a few lines:
import { PreviewFrame, useReevePresetForm } from '@meetreeve/preview-embed/react';
import { Input, Button } from 'your-design-system';
function ImessageEditor() {
const form = useReevePresetForm('imessage-conversation');
const messages = form.getArrayHelpers('messages');
return (
<div>
<Input {...form.getFieldProps('contactName')} placeholder="Contact name" />
{messages.items.map((_, i) => (
<div key={i}>
<select {...form.getFieldProps(`messages.${i}.sender`)}>
<option value="me">Me</option>
<option value="them">Them</option>
</select>
<Input {...form.getFieldProps(`messages.${i}.text`)} />
<Button onClick={() => messages.remove(i)}>Remove</Button>
</div>
))}
<Button onClick={() => messages.append()}>Add message</Button>
<PreviewFrame presetId="imessage-conversation" props={form.values} />
</div>
);
}messages.append() with no argument inserts a schema-derived blank
({ sender: 'me', text: '' } for iMessage). Pass messages.append({ sender: 'them' })
to merge a partial onto the blank.
Sender flips and sibling fields
Switching sender from 'them' to 'me' does not auto-scrub sibling fields
like emoji or readReceipt. If the new branch rejects them, you'll see entries
in form.errors at the relevant dot-path. Scrub them yourself if you want — the
SDK leaves data preservation as a consumer choice.
Schemas, defaults, and blankFromSchema
The full Zod schemas and registry-derived defaults are exported as plain values:
import { schemas, defaults, blankFromSchema, type PresetId } from '@meetreeve/preview-embed/react';
// All 28 preset IDs:
const ids = Object.keys(schemas) as PresetId[];
// Defaults seeded from src/presets/registry.ts defaultProps:
const initial = defaults['ad-product-showcase'];
// Walk a schema to derive a blank value:
const blankSlackMessage = blankFromSchema(schemas['slack-conversation'].shape.messages.element);Drop-in editor
import { ReevePreviewEditor, createReeveAIGenerator } from '@meetreeve/preview-embed/react';
import '@meetreeve/preview-embed/react/styles.css';
const aiGenerate = createReeveAIGenerator({
baseUrl: 'https://api.meetreeve.com',
getToken: async () => myAuth.getAccessToken(),
});
export default function MyEditor() {
return (
<ReevePreviewEditor
presetId="imessage-conversation"
aiGenerateHandler={aiGenerate}
imageUploadHandler={async (file) => (await myUpload(file)).url}
/>
);
}For fields-only (no preview iframe), use <ReeveEditor> with the same prop signature minus host/variant/layout/toolbar/footer.
For per-field customization:
<ReeveEditor
presetId="imessage-conversation"
overrides={{
contactAvatar: ({ value, onChange }) => <MyAvatarPicker value={value} onChange={onChange} />,
'messages.*.reaction': ({ value, onChange, descriptor }) => (
<MyReactionPicker
value={value}
onChange={onChange}
options={descriptor.enumOptions ?? []}
/>
),
}}
/>To theme, override CSS variables on .reeve-editor:
.my-app .reeve-editor {
--reeve-bg: var(--background);
--reeve-fg: var(--foreground);
--reeve-border: var(--border);
--reeve-accent: var(--primary);
--reeve-accent-fg: var(--primary-foreground);
--reeve-radius: var(--radius);
}Direct schema access
import { schemas, defaults } from '@meetreeve/preview-embed/react';
schemas['imessage-conversation'].safeParse(myValues);
console.log(defaults['imessage-conversation']);Substrate: auth, brand, surfaces
These moved to @meetreeve/embed in 2.0.0 (see top of file). See
MindFortressInc/reeve-embed
for the full docs. Quick pointers:
mintEmbedToken/verifyEmbedToken— authed HMAC JWTs for cross-host surfaces.@meetreeve/embed/authmintAnonEmbedToken/verifyAnonEmbedToken— anon-visitor tokens forchat.meetreeve.cometc.@meetreeve/embed/authcreateOriginAllowlist— postMessage origin allowlist helper.@meetreeve/embed/auth<ReeveSurface>/<ReeveCommsAdmin>— generic iframe surface + Comms admin wrapper.@meetreeve/embed/reactfetchHostAppBrand/brandToCssVars—host_app_brandsregistry client.@meetreeve/embed/brandBrandPack→ CSS vars (older Studio-derived).@meetreeve/embed/theme
License
UNLICENSED. Internal use within MindFortress + authorized white-label partners.
