Skip to content

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.

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.

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" }
]
}
FieldDescription
manifestFormatVersionAlways "1.0.0" for now. Required for zip uploads.
idA 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.
nameThe human-readable display name shown in Extension Manager and menus.
baseUrlThe base URL for resolving relative paths. For zip uploads, use "/". For localhost dev, something like https://localhost:9000. For remote hosting, your server URL.
mainFileYour entry point file, relative to baseUrl. This is generally a .html file and will be loaded directly into the extension’s iframe.
FieldDescription
iconPath to your extension icon (SVG recommended). Relative to baseUrl, or an absolute URL.
descriptionShort description shown in Extension Manager.
versionYour extension’s version string.
creatorAuthor name.
copyrightCopyright notice.
creatorIconURL to the author’s avatar image.
FieldDefaultDescription
disabledfalseIf 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.
lockedfalseIf 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.
loadAtLaunchfalseIf 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.
platformsallArray 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.

The window object controls how your extension’s panel appears:

{
"window": {
"type": "floating",
"width": 420,
"height": 600,
"right": 200,
"top": 55
}
}

Window types:

TypeDescription
"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 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").

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 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" }
]
}
]
}

Same format as menuItems, but these entries appear in the right-click context menu:

{
"contextMenuItems": [
{ "type": "item", "commandId": "geometry-report" }
]
}

If you’re coming from the Ruby API, here’s how manifest concepts map:

Ruby APIJSA 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_loadedHandled automatically by the loader