Non-React integration
@ydesign/react-editor is a React component library and needs a React runtime. That does not mean your host app must be React — if the main site is Vue / Angular / Svelte / Solid / vanilla JS, you can still embed Ydesign with the pattern below.
💡 On Vue 3, prefer the planned
@ydesign/vue-editoronce it ships — native Vue, no React runtime.
This page is for: Vue before@ydesign/vue-editorlands, or Angular / Svelte / other hosts.
Approach
Host app (Vue / Angular / Svelte / vanilla JS)
│
│ call createEditor({ container, ... })
▼
┌──────────────────────────────────────────┐
│ editor.js (standalone bundle) │
│ │
│ React + @ydesign/react-editor + your │
│ customizations │
└──────────────────────────────────────────┘
Build the editor as an isolated React subproject, expose a single function (recommended name: createEditor), and consume it from the host like any plain JS library. The host never sees React.
⚠️ Prerequisite: Customizing the editor still requires React skills (panels, buttons are React components). If the team has no React experience, wait for @ydesign/vue-editor or use the Ydesign Button iframe approach.
Examples below use parcel; Vite / Rspack / Webpack follow the same idea.
1. Create an isolated editor/ subproject
mkdir editor
cd editor
npm init -y
npm install react react-dom @ydesign/react-editor
npm install -D parcel
2. Dev shell: editor/index.html
Used only for local debugging — the host app does not load this file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Ydesign Editor - Dev</title>
<link rel="stylesheet" href="https://unpkg.com/@ydesign/react-editor/dist/style.css" />
<style>
body {
margin: 0;
}
#root {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.js"></script>
<script>
window.onload = () => {
window.createEditor({ container: document.getElementById('root') });
};
</script>
</body>
</html>
3. Entry: editor/index.js
Keep all React code here. Export only a plain createEditor function.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { DesignEditorContainer, SidePanelWrap, WorkspaceWrap, createStore, SidePanel } from '@ydesign/react-editor';
import Workspace from '@ydesign/react-editor/canvas/workspace';
import Toolbar from '@ydesign/react-editor/toolbar';
import ZoomButtons from '@ydesign/react-editor/toolbar/zoom-buttons';
// Customize here: swap panels, add buttons, theme, etc.
const Editor = ({ store }) => (
<DesignEditorContainer style={{ width: '100%', height: '100%' }}>
<SidePanelWrap>
<SidePanel store={store} />
</SidePanelWrap>
<WorkspaceWrap>
<Toolbar store={store} />
<Workspace store={store} />
<ZoomButtons store={store} />
</WorkspaceWrap>
</DesignEditorContainer>
);
/**
* Public API — the host only calls this
* @param {HTMLElement} container mount node
* @param {string} [key] API key
* @param {Function} [onReady] called after store is ready (host ↔ editor bridge)
*/
export const createEditor = ({ container, key, onReady }) => {
const store = createStore({ key: key || 'YOUR_API_KEY' });
const root = ReactDOM.createRoot(container);
root.render(<Editor store={store} />);
onReady?.(store);
return {
store,
destroy: () => root.unmount(),
};
};
// Dev: expose on window for the HTML shell
if (typeof window !== 'undefined') {
window.createEditor = createEditor;
}
4. Local development
npx parcel ./editor/index.html
Opens at http://localhost:1234 by default.
Recommended editor/package.json scripts:
{
"scripts": {
"dev": "parcel ./editor/index.html",
"build": "parcel build ./editor/index.js --no-source-maps"
},
"main": "dist/index.js"
}
5. Build for the host
npm run build
Produces editor/dist/index.js (React, @ydesign/react-editor, and your customizations).
6. Consume from the host
Treat editor/dist/index.js as a normal ES module regardless of the host framework.
Vue 3
<template>
<div ref="containerRef" class="editor-container" />
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { createEditor } from '../editor/dist/index.js';
import '@ydesign/react-editor/dist/style.css';
const containerRef = ref(null);
let editorInstance = null;
onMounted(() => {
editorInstance = createEditor({
container: containerRef.value,
key: import.meta.env.VITE_YDESIGN_KEY,
onReady: store => {
console.log('Editor ready', store);
},
});
});
onBeforeUnmount(() => {
editorInstance?.destroy();
});
</script>
<style scoped>
.editor-container {
width: 100%;
height: 100vh;
}
</style>
Angular (core fragment)
// editor.component.ts
import { Component, ElementRef, AfterViewInit, ViewChild, OnDestroy } from '@angular/core';
import { createEditor } from '../../../editor/dist';
@Component({
selector: 'app-editor',
template: '<div #host class="host"></div>',
styles: [
`
.host {
width: 100%;
height: 100vh;
}
`,
],
})
export class EditorComponent implements AfterViewInit, OnDestroy {
@ViewChild('host') host!: ElementRef<HTMLElement>;
private instance: any;
ngAfterViewInit() {
this.instance = createEditor({
container: this.host.nativeElement,
key: 'YOUR_API_KEY',
});
}
ngOnDestroy() {
this.instance?.destroy();
}
}
Vanilla JS / Svelte / Solid
Same pattern: import createEditor, pass a DOM node, call destroy on teardown.
7. Host ↔ editor communication
createEditor returns store — the full MobX-State-Tree instance:
const { store } = createEditor({ container, key });
// Host → editor: load a design
await fetch('/api/designs/42')
.then(r => r.json())
.then(json => store.loadJSON(json));
// Editor → host: react to changes
import { reaction } from 'mobx';
reaction(
() => store.toJSON(),
json => {
console.log('Design changed', json);
// autosave / push / dirty flag…
}
);
// Export image
const url = await store.toDataURL({ multiplier: 2 });
8. Pitfalls
- Dependency clashes: Keep the editor in
editor/with its ownpackage.jsonso React / Babel configs don’t fight the host. - Missing styles: Always load
@ydesign/react-editor/dist/style.css. - SSR: The editor must render on the client. In Nuxt / Next / SvelteKit wrap it with client-only boundaries (
<ClientOnly>,ssr: falsedynamic import, etc.). - Multiple instances: Multiple editors on one page are fine — they are isolated.
Related examples
- Vue 3 + Vite sample: repo
apps/demo-vue - Angular sample: repo
apps/demo-angular(planned)
If your case isn’t covered, open an Issue or use Ydesign Button — no React knowledge required, good for costly legacy migrations.