Skip to content

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.

PackageFramework
@topol.io/editorPlain JavaScript or TypeScript
@topol.io/editor-reactReact
@topol.io/editor-vueVue 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@alpha
bash
npm install @topol.io/editor-react@alpha
bash
npm install @topol.io/editor-vue@alpha

v1 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 options prop takes the contents of config directly (the component wraps it for you).
  • The component renders an absolutely positioned container that fills its parent (100% width, 100vh height), so place it inside a positioned wrapper sized to where the editor should appear.
  • The ref exposes load(template) and save(); 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: onSave becomes @on-save, onLoaded becomes @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

INFO

Running into trouble while integrating the NPM package? Contact our support team here and we'll help you get it working.