Skip to content

Plugins

Plugins add capabilities to the mesh. They have a lifecycle: setup runs when registered, cleanup runs when the mesh is destroyed.

Register a Plugin

ts
mesh.use({
  name: "my-plugin",
  setup(mesh) {
    // Subscribe to events, register middleware, etc.
    const unsub = mesh.on({ type: "state.changed" }, (event) => {
      analytics.track("state_change", event);
    });
    return () => unsub(); // cleanup function
  }
});

Plugin Interface

FieldTypeRequiredDescription
namestringYesUnique plugin name
setup(mesh) => void | (() => void)YesSetup function. Return a cleanup function.
cleanup() => voidNoCalled when the mesh is destroyed (alternative to returning from setup)

Duplicate Guard

Registering a plugin with the same name throws DuplicateRegistrationError:

ts
mesh.use({ name: "analytics", setup: () => {} });
mesh.use({ name: "analytics", setup: () => {} }); // Throws

Built-in Plugins

PluginImportDescription
persistPluginstatemesh-core/persistState persistence
tabSyncPluginstatemesh-core/syncCross-tab sync
loggerPluginstatemesh-core/devtoolsConsole logger
devtoolsBridgePluginstatemesh-core/devtoolsDevTools bridge

Example: Analytics Plugin

ts
function analyticsPlugin(trackingId: string) {
  return {
    name: "analytics",
    setup(mesh: Mesh) {
      const unsub = mesh.on({ type: "action.completed" }, (event) => {
        window.gtag("event", event.name, {
          event_category: "action",
          event_label: event.type
        });
      });
      return unsub;
    }
  };
}

mesh.use(analyticsPlugin("G-XXXXXXXXXX"));

Important Notes

TIP

Plugins are the recommended way to add cross-cutting concerns: analytics, logging, error reporting, and feature flags.

WARNING

Plugin setup runs synchronously when mesh.use() is called. If you need async initialization, handle it inside the setup function with a .then() or Promise.

Next Steps

Released under the MIT License.