Appearance
NPM Package Integration
The Getting Started guide embeds the editor with a plain <script> tag. In a project built around a bundler or a component framework, the official NPM packages do the same job with proper TypeScript types and lifecycle handling. The editor script is loaded for you, and in React and Vue the editor mounts and tears down together with the component that holds it.
Create your API token first
The packages wrap the editor, not the authorization. The editor still needs the Public API key from Step 1 in Getting Started.
| Package | Framework |
|---|---|
@topol.io/editor | Plain JavaScript or TypeScript |
@topol.io/editor-react | React |
@topol.io/editor-vue | Vue 3 |
Landing Page Editor support ships in the v1 alpha of these packages, published under the alpha tag on npm:
bash
npm install @topol.io/editor@alphabash
npm install @topol.io/editor-react@alphabash
npm install @topol.io/editor-vue@alphav1 is in alpha
The v1 packages are an early pre-release. We do not expect major changes to the API before the final release, but details can still move. Pin the exact version you integrate against and read the changelog before upgrading. The @topol.io/editor-svelte package does not include the Landing Page Editor yet.
Let an AI agent do the wiring
TOPOL-io/skills has two skills for coding agents, written as plain markdown. topol-editor-integration sets up either editor in a new project and topol-v1-upgrade moves an existing integration from 0.x to v1. They install as a Claude Code plugin (/plugin marketplace add TOPOL-io/skills) or into any agent with npx skills@latest add TOPOL-io/skills.
All packages take the same configuration as the script-tag integration: options go under config, callbacks are top-level properties. Everything documented in Editor Options applies unchanged.
JavaScript / TypeScript
@topol.io/editor exports a LandingPageEditor object with an async init() method. It loads the editor script, creates an instance, and resolves with it. The resolved instance is the same one the global LandingPageEditor() factory returns, with an added destroy() method for cleanup:
typescript
import { LandingPageEditor } from "@topol.io/editor";
const editor = await LandingPageEditor.init({
config: {
authorize: {
apiKey: "YOUR_API_KEY", // Public API key from your Topol account
userId: "user-123", // Any unique ID for your user
},
},
onSave(json, html) {
// `json` is the template definition, `html` the rendered page
console.log("Saved landing page:", json, html);
},
onInit() {
console.log("Landing Page Editor is ready");
},
});
// Render the editor into a container element
editor.render("#landing-page-editor");
// Optionally load a template
editor.load(templateJson);
// Clean up when the editor is no longer needed
editor.destroy();Two differences from the script-tag integration:
init()is asynchronous. It resolves once the editor script has loaded, and rejects if loading fails.- The instance has a
destroy()method that removes the editor and cleans up its resources. Call it when navigating away from the page that hosts the editor.
The container element works the same as in the script-tag setup, and still needs an explicit width and height:
html
<div id="landing-page-editor" style="width: 100%; height: 100vh;"></div>React
@topol.io/editor-react exports a LandingPageEditor component. It renders its own container, initializes the editor on mount, and destroys it on unmount. Callbacks are passed as props, and programmatic control (load, save) goes through a ref:
tsx
import { useRef } from "react";
import {
LandingPageEditor,
type LandingPageEditorRef,
type IReactLandingPageOptions,
} from "@topol.io/editor-react";
const options: IReactLandingPageOptions = {
authorize: {
apiKey: "YOUR_API_KEY",
userId: "user-123",
},
};
export default function PageBuilder() {
const editorRef = useRef<LandingPageEditorRef>(null);
const loadTemplate = (template: unknown) => {
editorRef.current?.load(template);
};
return (
<LandingPageEditor
ref={editorRef}
options={options}
onSave={(json, html) => {
console.log("Saved landing page:", json, html);
}}
onLoaded={() => {
// A good place to call editorRef.current?.load(template)
}}
/>
);
}Notes:
- The
optionsprop takes the contents ofconfigdirectly (the component wraps it for you). - The component renders an absolutely positioned container that fills its parent (
100%width,100vhheight), so place it inside a positioned wrapper sized to where the editor should appear. - The ref exposes
load(template)andsave(); unmounting the component destroys the editor. - Every editor callback is available as a prop:
onSave,onSaveAndClose,onLoaded,onInit,onClose,onPreview,onAlert,onError, and the rest of the callback set.
Vue
@topol.io/editor-vue exports a LandingPageEditor component for Vue 3. Configuration goes into the options prop, callbacks arrive as events, and load/save are exposed on the template ref:
vue
<script setup lang="ts">
import { ref } from "vue";
import {
LandingPageEditor,
type IVueLandingPageOptions,
} from "@topol.io/editor-vue";
const options: IVueLandingPageOptions = {
authorize: {
apiKey: "YOUR_API_KEY",
userId: "user-123",
},
};
const editorRef = ref<InstanceType<typeof LandingPageEditor> | null>(null);
const handleSave = ({ json, html }: { json: unknown; html: unknown }) => {
console.log("Saved landing page:", json, html);
};
const loadTemplate = (template: unknown) => {
editorRef.value?.load(template);
};
</script>
<template>
<LandingPageEditor
ref="editorRef"
:options="options"
@on-save="handleSave"
@on-loaded="loadTemplate(myTemplate)"
/>
</template>Notes:
- Callbacks that receive
(json, html)in the plain integration (onSave,onSaveAndClose,onBannerClick) arrive in Vue as a single event payload{ json, html }. - Event names follow Vue conventions:
onSavebecomes@on-save,onLoadedbecomes@on-loaded, and so on. - The component destroys the editor automatically before it unmounts.
TypeScript support
All three packages are written in TypeScript and export the Landing Page Editor types:
typescript
import type {
ILandingPageOptions, // full init() argument: { config, ...callbacks }
ILandingPageCallbacks, // just the callback signatures
ILandingPageEditorInstance, // render / load / save / destroy
TopolSection, // block payload used by onBlockSave
} from "@topol.io/editor";The framework packages re-export these, plus their own component types (IReactLandingPageOptions, LandingPageEditorRef in React; IVueLandingPageOptions in Vue).
Next steps
- Configure the editor with Editor Options
- Control it programmatically with the instance methods
- Migrating from the 0.x packages: NPM Packages: Upgrading from 0.x to v1 lists everything that changed.
- The Landing Page Editor lives in the same packages as a named export next to
EmailEditor, so one install covers both editors.
INFO
Running into trouble while integrating the NPM package? Contact our support team here and we'll help you get it working.
