Apache Superset's extension system is designed to enable powerful customization while maintaining stability, security, and performance. This page explains the architectural principles, system design, and technical mechanisms that make the extension ecosystem possible.
The extension architecture is built on six core principles that guide all technical decisions and ensure extensions can be developed safely and predictably:
Superset's core should remain minimal, with many features delegated to extensions. Built-in features use the same APIs and extension mechanisms available to external developers. This approach:
All extension points are clearly defined and documented. Extension authors know exactly where and how they can interact with the host system. Both backend and frontend contributions are registered directly in code — backend contributions via classes decorated with @api (and other decorators) imported from the auto-discovered entrypoint, frontend contributions via calls like views.registerView and commands.registerCommand executed at module load time in index.tsx. This gives the host clear visibility into what each extension provides:
Public interfaces for extensions follow semantic versioning, allowing for:
Extensions are loaded and activated only when needed, which:
The architecture encourages reusing extension points and patterns across different modules, promoting:
The system evolves based on real-world feedback and contributions. New extension points and capabilities are added as needs emerge, ensuring the platform remains relevant and flexible.
The extension architecture is built around three main components that work together to create a flexible, maintainable ecosystem:
Two core packages provide the foundation for extension development:
Frontend: @apache-superset/core
This package provides essential building blocks for frontend extensions and the host application:
By centralizing these resources, both extensions and built-in features use the same APIs, ensuring consistency, type safety, and a seamless user experience. The package is versioned to support safe platform evolution while maintaining compatibility.
Backend: apache-superset-core
This package exposes key classes and APIs for backend extensions:
It includes dependencies on critical libraries like Flask-AppBuilder and SQLAlchemy, and follows semantic versioning for compatibility and stability.
apache-superset-extensions-cli
The CLI provides comprehensive commands for extension development:
By standardizing these processes, the CLI ensures extensions are built consistently, remain compatible with evolving versions of Superset, and follow best practices.
The Superset host application serves as the runtime environment for extensions:
Extension Management
/api/v1/extensions endpoint for registration and managementextensions database tableExtension Storage
The extensions table contains:
The following diagram illustrates how these components work together:
The diagram shows:
One of the most sophisticated aspects of the extension architecture is how frontend code is dynamically loaded at runtime using Webpack's Module Federation.
The architecture leverages Webpack's Module Federation to enable dynamic loading of frontend assets. This allows extensions to be built independently from Superset.
Extension Configuration
Extensions configure Webpack to expose their entry points:
plugins: [ new ModuleFederationPlugin({ name: 'my_extension', filename: 'remoteEntry.[contenthash].js', exposes: { './index': './src/index.tsx', }, shared: { react: { singleton: true, import: false }, 'react-dom': { singleton: true, import: false }, antd: { singleton: true, import: false }, '@apache-superset/core': { singleton: true, import: false }, }, }), ]
This configuration does several important things:
exposes - Declares which modules are available to the host application. Superset always loads extensions by requesting the ./index module from the remote container — this is a fixed convention, not a configurable value. Extensions must expose exactly './index': './src/index.tsx' and place all API registrations (views, commands, menus, editors, event listeners) in that file. The module is executed as a side effect when the extension loads, so any call to views.registerView, commands.registerCommand, etc. made at the top level of index.tsx will run automatically.
shared - Prevents duplication of common libraries like React and Ant Design, and, for @apache-superset/core, is the mechanism that gives each extension an isolated context (see below). The singleton: true setting ensures only one logical instance of each library exists — for react/react-dom/antd that means the host‘s actual instance is reused; for @apache-superset/core it means the extension’s container defers to whatever module the host's loader supplies for it at init time, which is not the same object for every extension (see Runtime Resolution).
The following diagram illustrates the module loading process:
Here's what happens at runtime:
__webpack_init_sharing__) and looks up the extension's container on windowcontainer.init(), the loader builds a per-container copy of the webpack share scope in which the @apache-superset/core entry is replaced with a synthetic module — a copy of the host‘s own window.superset implementations with extensions.getContext bound to that one extension’s isolated context object. Because Module Federation caches the resolved module per container, every import of @apache-superset/core inside that extension resolves to this pre-bound copy for the lifetime of the container, and because each container gets its own independent scope object, extensions loading in parallel cannot see each other's context.@apache-superset/core instance) and shared dependencies (react, react-dom, antd)On the Superset side, the real implementations backing @apache-superset/core (commands, views, menus, extensions, etc.) are assigned onto window.superset during application bootstrap, once the current user is known:
// eslint-disable-next-line no-restricted-syntax import * as supersetCore from '@apache-superset/core'; import { commands, views, menus, extensions /* ... */ } from 'src/core'; window.superset = { ...supersetCore, commands, views, menus, extensions, // ... };
This runs before any extensions are loaded. window.superset is not itself what an extension‘s @apache-superset/core import resolves to — it is the source object the loader reads from when building each extension’s per-container scoped instance (see Runtime Resolution), so every extension ends up sharing the same underlying commands/views/menus singletons but with its own extensions.getContext().
This architecture provides several key benefits:
Now that you understand the architecture, explore: