The Manifest
Every JSA extension needs a manifest.json file. It tells SketchUp who your extension is, what it can do, and how to display it. Think of it like a plugin.rb registration block, but in JSON.
A minimal example
Section titled “A minimal example”Here’s the simplest possible manifest — an extension with one menu item that opens a floating panel:
{ "manifestFormatVersion": "1.0.0", "id": "hello-world", "name": "Hello World", "baseUrl": "/", "mainFile": "index.html", "window": { "type": "floating" }, "commands": { "open": { "title": "Hello World" } }, "menuItems": [ { "type": "item", "commandId": "open" } ]}That’s it. SketchUp will add a “Hello World” entry to the Extensions menu. When the user clicks it, your index.html loads in a floating panel.
A full example
Section titled “A full example”Here’s a more realistic manifest showing most of the available options:
{ "manifestFormatVersion": "1.0.0",
"id": "model-health-check", "name": "Model Health Check", "baseUrl": "/", "mainFile": "model-health-check.js",
"icon": "img/health-check.svg", "description": "Run checks on your model health.", "version": "1.0.0", "creator": "Your Name", "copyright": "2026 Your Company",
"window": { "type": "floating", "width": 420, "height": 600, "right": 200, "top": 55 },
"commands": { "open-dashboard": { "title": "Model Health Check", "description": "Open the health-check dashboard.", "icon": "img/health-check.svg" }, "geometry-report": { "title": "Geometry Report", "description": "Scan for reversed faces and stray edges.", "icon": "img/geometry.svg" } },
"menuItems": [ { "type": "item", "commandId": "open-dashboard" }, { "type": "divider" }, { "type": "subMenu", "title": "Reports", "menuItems": [ { "type": "item", "commandId": "geometry-report" } ] } ],
"toolbars": [ { "title": "Health Check", "toolbarItems": [ { "type": "item", "commandId": "open-dashboard" }, { "type": "item", "commandId": "geometry-report" } ] } ],
"contextMenuItems": [ { "type": "item", "commandId": "geometry-report" } ]}Field reference
Section titled “Field reference”Required fields
Section titled “Required fields”| Field | Description |
|---|---|
manifestFormatVersion | Always "1.0.0" for now. Required for zip uploads. |
id | A unique kebab-case identifier for your extension (e.g. "model-health-check"). INTERNAL NOTE: the id will be autogenerated as a guid once the JSA upload is integrated into the Extension Warehouse. For now, you can just create something that’s likely to be unique. |
name | The human-readable display name shown in Extension Manager and menus. |
baseUrl | The base URL for resolving relative paths. For zip uploads, use "/". For localhost dev, something like https://localhost:9000. For remote hosting, your server URL. |
mainFile | Your entry point file, relative to baseUrl. This is generally a .html file and will be loaded directly into the extension’s iframe. |
Metadata (all optional)
Section titled “Metadata (all optional)”| Field | Description |
|---|---|
icon | Path to your extension icon (SVG recommended). Relative to baseUrl, or an absolute URL. |
description | Short description shown in Extension Manager. |
version | Your extension’s version string. |
creator | Author name. |
copyright | Copyright notice. |
creatorIcon | URL to the author’s avatar image. |
Lifecycle (all optional)
Section titled “Lifecycle (all optional)”| Field | Default | Description |
|---|---|---|
disabled | false | If true, the extension is skipped during load. INTERNAL NOTE: This setting is only for internal testing right now. It is being reworked as we productionize. |
locked | false | If true, the extension can’t be uninstalled (for system extensions). INTERNAL NOTE: This setting is only for internal testing right now. It is being reworked as we productionize. |
loadAtLaunch | false | If true, the iframe is created at startup. Essential for headless extensions that need to run without user interaction. INTERNAL NOTE: This setting is only for internal testing right now. It is being reworked as we productionize. |
platforms | all | Array of "web", "desktop", "ios". Omit to support all platforms. |
parentMenu | "Extensions" | Which menu your items appear under. One of "Extensions", "Export", "Import" or "Download" — any other value falls back to "Extensions". This is not a path and does not create nested folders; use a subMenu entry in menuItems to nest. INTERNAL NOTE: This setting is only for internal testing right now. It is being reworked as we productionize. |
Window
Section titled “Window”The window object controls how your extension’s panel appears:
{ "window": { "type": "floating", "width": 420, "height": 600, "right": 200, "top": 55 }}Window types:
| Type | Description |
|---|---|
"headless" | No visible window. Your code runs in the background. Good for extensions that just respond to menu clicks or process data silently. |
"floating" | A draggable panel that floats over the model. The most common choice for tool panels. |
"modal" | A dialog that blocks interaction with the model until dismissed. Use sparingly. |
"sidebar" | Docked to the side of the viewport. |
"tab" | Coming soon! SketchUp for Web will soon support “full tab” extensions. Stay tuned! |
Positioning: Use left/top or right/bottom to position the window. If you specify both left and right, left wins. Omit positioning entirely to center the window.
Commands
Section titled “Commands”Commands are the actions your extension can perform. You define them once and reference them by ID in menus, toolbars, and context menus.
{ "commands": { "open-dashboard": { "title": "Model Health Check", "description": "Open the health-check dashboard.", "icon": "img/health-check.svg" } }}When a user clicks a menu item or toolbar button tied to a command, your extension receives an extension_menu_action message with the command ID (e.g. "open-dashboard").
Menu items
Section titled “Menu items”The menuItems array defines what appears in the Extensions menu (or wherever parentMenu points). Three entry types: items, dividers, and nested submenus.
{ "menuItems": [ { "type": "item", "commandId": "open-dashboard" }, { "type": "item", "commandId": "set-dark", "checked": true }, { "type": "divider" }, { "type": "subMenu", "title": "Reports", "menuItems": [ { "type": "item", "commandId": "geometry-report" } ] } ]}Toolbars
Section titled “Toolbars”Toolbars put buttons in the toolbar area. Each toolbar has a title and a list of items referencing your commands:
{ "toolbars": [ { "title": "My Tools", "toolbarItems": [ { "type": "item", "commandId": "open-dashboard" }, { "type": "item", "commandId": "geometry-report" } ] } ]}Context menu items
Section titled “Context menu items”Same format as menuItems, but these entries appear in the right-click context menu:
{ "contextMenuItems": [ { "type": "item", "commandId": "geometry-report" } ]}Tips for Ruby developers
Section titled “Tips for Ruby developers”If you’re coming from the Ruby API, here’s how manifest concepts map:
| Ruby API | JSA Manifest |
|---|---|
SketchupExtension.new(name, ...) | "name" and "id" fields |
UI.menu("Extensions").add_item(...) | "menuItems" array |
UI::Command.new(...) | "commands" object |
toolbar.add_item(cmd) | "toolbars" array |
UI::HtmlDialog.new(...) | "window" object |
file_loaded? / file_loaded | Handled automatically by the loader |