Skip to main content
A legacy expansion is a main.js with module.exports.metadata and init(), using the global api. Those still load, marked Legacy on the Expansions page, but they are deprecated and the SDK is where new work goes.

The shape

Steps:
  1. Move metadata into meta.json. Add sdkVersion (^2.0.0) and permissions (see below). Add config.json.
  2. Replace init() with top-level code in the start file, or a class if you prefer.
  3. Replace path strings with imports: __dirname + '/pages/X.jsx' becomes import X from './pages/X'.
  4. In components, replace the props api, useComponents() and useProjectState with imports from sdk and sdk/ui.
  5. Delete api.expansion.call(...): logic and UI are one module graph now, so just import the function.

Point by point

Permissions

Legacy expansions had none: api could reach the whole project and the filesystem. In the SDK, declare what you use:
  • reading items or project files: project:read
  • writing them: project:write
  • fetch: network
  • clipboard: clipboard

Not available yet

These have no SDK point, so an expansion that depends on them stays legacy for now:
  • Export formats: postExportFormat, registerExportFormat, extendExistingFormat, registerExporter
  • Server reload commands: postReloadCommand
  • postTemplate, postBlueprintCategory, postBlueprintPiece
  • postAssets, postEditorModuleOverrides, postItemTypeStates, postCreatorTypeCompatibilities, postSettingsElementType
  • execBlockbench, and loadFile on paths outside the expansion
  • registerBackgroundModule; background scripts (sdk.run) are planned

Things that change quietly

  • Ids are namespaced. A page registered as counter is my_expansion.counter, and its route changes to match. Anything you hard-coded needs updating.
  • Imports are real. Helpers, shared files and CSS work now, which is usually a chance to delete the workarounds the old modulars needed.
  • Cleanup is automatic. Anything you registered is removed on reload or disable; remove hand-rolled teardown.
  • No Node. Any require('fs') or child process work has no equivalent and must go through the SDK, or stay legacy.