Skip to content

Extension guides

Package structure

The Pi entry runs under Node with full extension capabilities. The renderer entry runs as browser code inside NativePi. Keep their imports and responsibilities separate.

Recommended layout

my-package/
├── package.json
└── src/
    ├── extension.ts   # ordinary Pi extension
    ├── renderer.tsx   # NativePi browser renderer
    └── protocol.ts    # JSON schemas shared by both

Manifest

package.json
{
  "name": "@acme/my-package",
  "type": "module",
  "keywords": ["pi-package"],
  "pi": {
    "extensions": ["./src/extension.ts"]
  },
  "nativepi": {
    "renderer": "./src/renderer.tsx"
  }
}

NativePi discovers packages through Pi's configured package list, reads nativepi.renderer, compiles that browser entry with esbuild, then validates its default export. The path is relative to the package root.

Entry boundary

  • Pi entry: may use Node APIs and Pi's extension API. It must not import React components or renderer-only modules.
  • Renderer entry: may use React, shared UI, the renderer context, and browser-safe dependencies. It must not import Node APIs or Pi's extension runtime.
  • Shared protocol: contains only schemas and the protocol definition. Its runtime values must work in both processes.

Dependencies

Install @nativepi/extension-api as a normal dependency if the Pi entry imports /host or the protocol module imports it at runtime. Pi installs production dependencies for distributed packages, so a dev dependency would be unavailable to that entry.

A purely visual renderer may use it as a dev dependency. NativePi supplies React and extension API host modules while bundling, ensuring every renderer uses the window's existing React instance. Other renderer dependencies are included in the renderer bundle.

Load behavior

The ordinary Pi entry and graphical renderer fail independently. A missing, incompatible, or broken renderer produces a NativePi package error but does not prevent the Pi extension from loading. A package without nativepi.renderer remains an ordinary Pi package.

Continue with Typed protocols if the two entries need to communicate.