Skip to content

Activation events

Activation events tell the host when to load an extension's main JS. Loading is lazy: the sandbox boots and activate() runs only when one of the declared events fires. This keeps Aero light — code you don't use never loads.

When you need them

You need activation events only if your extension has a main (i.e. command logic). Theme-only, snippet-only, and keybinding-only extensions need no activation events and no main — their declarative contributions register directly from the manifest at install/enable time.

Extension ships…mainactivationEvents
Themes / snippets / keybindingsnono
Commands with JS handlersyesyes

The events

Declare them in aero.json:

json
"activationEvents": ["onCommand:dracula-warm.apply"]
EventFires when…
onCommand:<commandId>the user runs that command. The common case.
onStartupthe IDE finishes booting. Use sparingly.
*eager — loads immediately. Discouraged; reserve for tiny extensions.

The schema validates each event against ^(onCommand:[A-Za-z0-9_.-]+|onStartup|\*)$.

onCommand:<commandId>

The recommended pattern. The host loads main the first time the user runs the command (from the palette or a keybinding), then calls activate(context) and your registered handler. One activation event per command you handle:

json
{
  "main": "extension.js",
  "activationEvents": [
    "onCommand:my-ext.format",
    "onCommand:my-ext.toggle"
  ]
}

onStartup

Fires once when the IDE finishes booting. Use it only if your extension genuinely needs to do work before the user invokes anything (rare in v1). Prefer onCommand: so you stay out of the boot path.

* (eager)

Loads the extension immediately, every launch. Discouraged — it defeats lazy loading and adds to startup. Reserve for very small extensions where the cost is negligible.

The activation lifecycle

When an activation event fires, the host:

  1. boots the sandboxed <iframe> for the extension (one in-flight activation per extension is enforced);
  2. loads main and calls activate(context);
  3. emits ext:activated { id } on AeroBus.

Your activate receives a context and should push every Disposable it creates onto context.subscriptions so the host can clean them up on deactivate:

js
'use strict';

function activate(context) {
  const sub = aero.commands.registerCommand('my-ext.format', () => {
    /* … */
  });
  context.subscriptions.push(sub);
}

// Optional. Called when the extension is disabled/uninstalled.
function deactivate() {}

The context shape ({ extensionId, subscriptions }) is documented in The aero.* API.

Lean, AI-ready, under your control.