Penpot Opens Its Design Tool to Plugins

Penpot, the open-source design platform built on web standards, has introduced a plugin system that lets teams extend its capabilities without forking the codebase. The new system follows the project’s broader mission: giving designers and developers a shared, transparent workspace while keeping the tooling flexible enough for custom workflows.

The platform already supports collaborative prototyping, design systems at scale, and generates developer-ready code. It runs in the browser and has gained over 33K stars on GitHub. Its UI is accessible enough for non-designers while matching the output quality of closed-source alternatives like Figma.

Why Open Source Matters Here

For teams that rely on commercial open-source software, the appeal goes beyond cost. The code is auditable, which means quirks and UX gaps can be investigated and addressed directly. Developers often go beyond filing issues; they propose fixes. Self-hosting is another option, offering more privacy, control, or cost savings for teams willing to manage their own infrastructure.

When the maintainers can sustain that model, it’s a win for both the vendor and the users. That’s the context for the plugin system’s arrival.

Building a Plugin

Penpot now hosts a plugin gallery and template library at PenpotHub. If a feature is missing, the intended path is to write a plugin rather than modify the source. Plugins can even be served from localhost during development.

Getting started is straightforward. The project provides templates for several frameworks — one contributed for SolidJS via a pull request. Since plugins are Single-Page Applications, anyone familiar with Vite can build one. Penpot also ships packages to jumpstart development, including CSS loaders and the @penpot/plugin-styles/styles.css import.

The plugin API is exposed on the window object. TypeScript users need to add the provided types to node_modules, then reference them in tsconfig.json under compilerOptions.types. Once that’s wired up, the editor’s language service and the TypeScript compiler understand the penpot namespace, giving auto-completion across the project.

penpot.ui.open("Your Plugin Name", "", {
  width: 500,
  height: 600
})

A plugin requires a manifest.json in the Vite output directory, specifying where assets live and what permissions the plugin needs.

{
  "name": "Your Plugin Name",
  "description": "A Super plugin that will win Penpot Plugin Contest",
  "code": "/plugin.js",
  "icon": "/icon.png",
  "permissions": [
    "content:read",
    "content:write",
    "library:read",
    "library:write",
    "user:read",
    "comment:read",
    "comment:write",
    "allow:downloads"
  ]
}

Communication between the plugin and the Penpot API works over a bidirectional messaging channel, similar to a Web-Worker setup. Sending a message uses the API’s send methods; receiving responses requires an event listener on the window object at the top level of the plugin scope.

penpot.ui.sendMessage("Hello from my Plugin");
window.addEventListener("message", event => {
  console.log("Received from Pendpot::: ", event.data);
})

For more complex plugins with multiple views or routes, cleanup logic is essential to avoid stale listeners. React handles this through effect return statements; SolidJS provides onMount and onCleanup, or the @solid-primitive/event-listener helper for automatic disposal.

Official Guidance and a Contest

The official documentation includes a step-by-step guide covering creation, testing, and publishing. To encourage adoption, Penpot is running a plugin contest from November 15 to December 15. Entries must be fully functional, open-source, and documented — covering features, installation, and usage. Judging criteria include innovation, functionality, usability, performance, and code quality, with a US$ 1,000 first prize.