Understand how CE.SDK’s plugin system fits together on Android—what a plugin is, how it chains onto an editor configuration, and which parts of the editor it can extend.
A plugin is a self-contained unit that packages editor behavior—callbacks, state, UI component configuration—and attaches to an existing editor without rebuilding it. On Android, a plugin is a class inheriting from EditorConfigurationBuilder that is chained onto the editor configuration. IMG.LY ships official plugins such as background removal this way, and you can build your own.
This guide covers what a plugin is on Android, when to use one instead of other customization mechanisms, how plugins compose through the configuration chain, and where each official plugin is documented.
What a Plugin Is#
The editor configuration is built first, and plugins are chained onto it afterwards. Each plugin is one EditorConfigurationBuilder subclass with one place where it applies its behavior: the builder properties it overrides. The same composition idea exists on every platform, even though the APIs differ—the Web registers plugin objects with addPlugin() and iOS subclasses EditorConfiguration.
Plugin or Not?#
Plugins are one of several customization mechanisms, and they’re commonly conflated. Use this list to place them:
- Inline configuration: The default. Configure the editor directly in the builder block where it’s created—best for one editor surface or a one-off product flow.
- Starter kits: Copy-to-adapt scaffolds for complete editor experiences. Browse them in the starter kits overview.
- Plugins: When the same behavior must travel between projects or entry points as one unit.
Importers and exporters are standalone packages that convert file formats. They are versioned independently of CE.SDK and are not plugins—you use their own APIs instead of chaining them onto the editor configuration.
How a Plugin Attaches#
The base configuration comes first, plugins second, app-specific edits last. EditorConfiguration.remember creates the base, and then(::SomePlugin) chains a plugin onto it. The builder block passed to then configures plugin options for this editor entry point:
Editor( license = license, configuration = { EditorConfiguration .remember(::DesignConfigurationBuilder) .then(::CustomFeaturePlugin) { randomImageUri = "https://img.ly/static/ubq_samples/sample_1.jpg".toUri() } }, onClose = onClose,)Each plugin in the chain can reach the previous configuration through parentConfiguration, so it decides per callback and per component whether to extend the inherited behavior or replace it.
Extending and Replacing Behavior#
A plugin that overrides onCreate can run work around the inherited setup by invoking parentConfiguration?.onCreate in the middle:
override var onCreate: (suspend EditorScope.() -> Unit)? = { try { isLoading = true val editorScope = this coroutineScope { launch { parentConfiguration?.onCreate?.invoke(editorScope) // The parent Design configuration hides loading after scene setup. isLoading = true } launch { Log.d(TAG, "CustomFeaturePlugin setup started.") delay(3_000) Log.d(TAG, "CustomFeaturePlugin setup finished.") } } } finally { isLoading = false }}The same choice applies to UI components. This dock override keeps the inherited dock and prepends one button instead of replacing the whole component:
override var dock: ScopedProperty<EditorScope, EditorComponent<*>?>? = dockComponent@{ val sourceDock = parentConfiguration?.dock as? Dock ?: return@dockComponent null val updatedListBuilder = sourceDock.listBuilder.modify { addFirst { Dock.Button.remember { id = { EditorComponentId("com.example.component.dock.button.customFeature") } vectorIcon = { IconPack.Image } textString = { "Image" } contentDescription = { "Add image" } onClick = { addImageBlockFromPlugin() } } } } remember(sourceDock, updatedListBuilder) { sourceDock.copy(listBuilder = updatedListBuilder) }}Plugin Options#
Plugins expose options as builder properties, so the same plugin can behave differently in different editors. State that participates in Compose recomposition uses editorContext.mutableStateOf:
var randomImageUri: Uri by editorContext.mutableStateOf( key = "com.example.editor.customFeature.randomImageUri", initial = Uri.parse("https://img.ly/static/ubq_samples/sample_1.jpg"),)
private var isLoading: Boolean by editorContext.mutableStateOf( key = BasicConfigurationBuilder.KEY_STATE_SHOW_LOADING, initial = false,)Official Plugins#
Each official plugin has its own page with installation and options; this table only maps the landscape.
| Plugin | What it does | Docs |
|---|---|---|
| Background Removal | Removes image backgrounds on device | Remove Background |
| AI Generation | Generates images and more in the editor | AI Image Generation |
The plugins section lists the full set.
Building Your Own#
A custom plugin is an EditorConfigurationBuilder subclass with a stable identity, options as builder properties, and overridden callbacks or components. Reach for one when the same behavior needs to travel together instead of being pasted into every editor setup. The Custom Feature Plugin guide walks through building the plugin shown on this page.
Troubleshooting#
- The editor opens without the expected scene: A plugin replaced
onCreateand skipped the base setup. Delegate toparentConfiguration?.onCreate, or fully create the scene inside the plugin. - A dock or inspector item disappears: The plugin replaced a component instead of extending it. Derive from
parentConfiguration?.dockwhen adding controls. - Plugin options don’t update: The option is captured once in the builder. Store dynamic values with
editorContext.mutableStateOfso they participate in state.
Next Steps#
- Custom Feature Plugin - Build your own plugin
- Architecture - How the CreativeEngine is structured
- Starter Kits - Complete editor scaffolds to start from