The dock provides quick access to content libraries and editing tools, appearing at the bottom of the editor interface. This guide shows you how to customize dock items and their layout to match your app’s content strategy and user workflow.
Explore a complete code sample on GitHub.
Dock Architecture#

The dock displays horizontally at the bottom of the editor and provides quick access to content libraries and editing tools. It adapts its content based on the selected editor solution.
Key Components:
Dock.Button- Pre-built button implementation with icon and textDock.Scope- Provides access to the engine, event handler, and editor context
Configuration#
Dock customization is part of the EditorConfiguration, therefore, in order to configure the dock we need to configure the EditorConfiguration.
Below you can find the list of available configurations of the dock.
To demonstrate the default values, all properties are assigned to their default values unless specified otherwise.
Available configuration properties:
-
scope- Scope of this component. Every new value will trigger recomposition of allScopedPropertys such asvisible,enterTransition,exitTransitionetc. Consider using Composeandroidx.compose.runtime.Stateobjects in the lambdas for granular recompositions over updating the scope, since scope change triggers full recomposition of the component. Ideally, scope should be updated when the parent scope (scope of the parent component) is updated and when you want to observe changes from theEngine. By default the scope is updated only when the parent scope is updated. -
visible- Control dock visibility. Default value is always true. -
modifier- Jetpack Compose modifier of this component. By default empty modifier is applied. -
enterTransition- Animation for when the dock appears. Default value is always no enter transition. -
exitTransition- Animation for when the dock disappears. Default value is always no exit transition. -
decoration- Apply custom styling like background, shadows, and padding. The default decoration adds background color and applies paddings to the dock. -
listBuilder- Define the complete list of dock items and their order. Items are only displayed whenvisiblereturnstrue. By default,listBuilderdoes not add anything to the dock. -
horizontalArrangement- Layout arrangement for dock items. Controls spacing and alignment of items within the dock. Default value isArrangement.Start. -
itemDecoration- decoration of the items in the dock. Useful when you want to add custom background, foreground, shadow, paddings etc to the items. Prefer using this decoration when you want to apply the same decoration to all the items, otherwise set decoration to individual items. Default value is always no decoration.
The Dock.Scope (this instance in all lambdas) provides access to the engine, event handler, and editor context. Use this for advanced customization logic and to maintain consistency with the current editor state.
EditorConfiguration.remember { dock = { Dock.remember { scope = { remember(this) { Dock.Scope(parentScope = this) } } modifier = { Modifier } visible = { true } enterTransition = { EnterTransition.None } exitTransition = { ExitTransition.None } decoration = { // Also available via Dock.DefaultDecoration Box( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surface1.copy(alpha = 0.95f)) .padding(vertical = 10.dp), ) { it() } } listBuilder = { Dock.ListBuilder.remember { /* Add items */ } } horizontalArrangement = { Arrangement.SpaceEvenly } // Default value is { it() } itemDecoration = { Box(modifier = Modifier.padding(2.dp)) { it() } } } }}ListBuilder Configuration#
You can configure the items of the dock using two main approaches:
New List Builder#
The simplest approach is to create a new list builder from scratch. In this example, we add both already declared and custom buttons:
Dock.ListBuilder.remember { add { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.custom") } vectorIcon = null textString = { "Custom Button" } onClick = {} } } add { Dock.Button.rememberSystemGallery() } add { Dock.Button.rememberSystemCamera() } add { Dock.Button.rememberElementsLibrary() } add { Dock.Button.rememberStickersLibrary() } add { Dock.Button.rememberImagesLibrary() } add { Dock.Button.rememberTextLibrary() }}All available predefined buttons are listed below.
Modify Existing List Builder#
In case you already have an existing list builder and want to make simple modifications on it (prepend, append, replace, insert or remove an item) without touching the order invoke modify on the ListBuilder.
// Makes sense to use only with builders that are already available and cannot be modified by you directly.val existingListBuilder = Dock.ListBuilder.remember { add { Dock.Button.rememberSystemGallery() } add { Dock.Button.rememberSystemCamera() } add { Dock.Button.rememberTextLibrary() } add { Dock.Button.rememberShapesLibrary() }}existingListBuilder.modify { addFirst { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.first") } vectorIcon = null textString = { "First Button" } onClick = {} } } addLast { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.last") } vectorIcon = null textString = { "Last Button" } onClick = {} } } addAfter(id = Dock.Button.Id.systemGallery) { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.afterSystemGallery") } vectorIcon = null textString = { "After System Gallery" } onClick = {} } } addBefore(id = Dock.Button.Id.systemCamera) { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.beforeSystemCamera") } vectorIcon = null textString = { "Before System Camera" } onClick = {} } } replace(id = Dock.Button.Id.textLibrary) { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.replacedTextLibrary") } vectorIcon = null text = { "Replaced Text Library" } onClick = {} } } remove(id = Dock.Button.Id.shapesLibrary)}Available modification operations:
addFirst- prepends newDock.Item:
addFirst { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.first") } vectorIcon = null textString = { "First Button" } onClick = {} }}addLast- appends newDock.Item:
addLast { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.last") } vectorIcon = null textString = { "Last Button" } onClick = {} }}addAfter- adds newDock.Itemright after the item with the provided id:
addAfter(id = Dock.Button.Id.systemGallery) { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.afterSystemGallery") } vectorIcon = null textString = { "After System Gallery" } onClick = {} }}addBefore- adds newDock.Itemright before the item with the provided id:
addBefore(id = Dock.Button.Id.systemCamera) { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.beforeSystemCamera") } vectorIcon = null textString = { "Before System Camera" } onClick = {} }}replace- replaces theDock.Itemwith the provided id with new item:
replace(id = Dock.Button.Id.textLibrary) { Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.replacedTextLibrary") } vectorIcon = null text = { "Replaced Text Library" } onClick = {} }}remove- removes theDock.Itemwith the provided id:
remove(id = Dock.Button.Id.shapesLibrary)Dock.Item Configuration#
Each Dock.Item is an EditorComponent. Its id must be unique which is a requirement for proper component management.
Depending on your needs there are multiple ways to define an item. In this example, we demonstrate your options with increasing complexity.
Use Predefined Buttons#
The most basic option is to use our predefined buttons which are provided as composable functions. All available predefined buttons are listed below.
Create New Buttons#
If our predefined buttons don’t fit your needs you can create your own. To demonstrate the default values, all properties are assigned to their default values unless specified otherwise:
@Composablefun rememberDockButton() = Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.newButton") } scope = { remember(this) { Dock.ItemScope(parentScope = this) } } modifier = { Modifier } visible = { true } enterTransition = { EnterTransition.None } exitTransition = { ExitTransition.None } // Default value is { it() } decoration = { Surface(color = MaterialTheme.colorScheme.background) { it() } } onClick = { editorContext.eventHandler.send(EditorEvent.Sheet.Open(SheetType.Volume())) } // Default value is null icon = { Icon( imageVector = IconPack.Music, contentDescription = null, ) } // Default value is null text = { Text( text = "Hello World", ) } enabled = { true }}Required and optional properties:
-
id- the id of the button. Note that it is highly recommended that every uniqueEditorComponenthas a unique id. By default property contains a random value. -
scope- scope of this component. Every new value will trigger recomposition of allScopedPropertys such asvisible,enterTransition,exitTransitionetc. Consider using Composeandroidx.compose.runtime.Stateobjects in the lambdas for granular recompositions over updating the scope, since scope change triggers full recomposition of the component. Ideally, scope should be updated when the parent scope (scope of the parent component) is updated and when you want to observe changes from theEngine. By default the scope is updated only when the parent component scope is updated. -
modifier- Jetpack Compose modifier of this component. By default empty modifier is applied. -
visible- whether the button should be visible. Default value is always true. -
enterTransition- transition of the button when it enters the parent composable. Default value is always no enter transition. -
exitTransition- transition of the button when it exits the parent composable. Default value is always no exit transition. -
decoration- decoration of the button. Useful when you want to add custom background, foreground, shadow, paddings etc. Default value is always no decoration. -
onClick- the callback that is invoked when the button is clicked. By default it is a no-op. -
icon- the icon content of the button. If null, it will not be rendered. Default value is null. -
text- the text content of the button. If null, it will not be rendered. Default value is null. -
tint- the tint color of the content. By default it isMaterialTheme.colorScheme.onSurfaceVariant. -
enabled- whether the button is enabled. Default value is always true.
This gives full control over the content of the button. However, there are simpler configuration options if you do not want to fully customize text and icon composables. Let’s have a look at this example:
@Composablefun rememberDockButtonSimple() = Dock.Button.remember { id = { EditorComponentId("my.package.dock.button.newButton") } scope = { remember(this) { Dock.ItemScope(parentScope = this) } } modifier = { Modifier } visible = { true } enterTransition = { EnterTransition.None } exitTransition = { ExitTransition.None } // Default value is { it() } decoration = { Surface(color = MaterialTheme.colorScheme.background) { it() } } onClick = { editorContext.eventHandler.send(EditorEvent.CloseEditor()) } // Default value is null vectorIcon = { IconPack.Music } // Default value is null textString = { "Hello World" } tint = { MaterialTheme.colorScheme.onSurfaceVariant } enabled = { true } contentDescription = null}It has three differences:
iconis replaced withvectorIconlambda, that returnsImageVectorinstead of drawing the icon content.textis replaced withtextStringlambda, that returnsStringinstead of drawing the text content.contentDescriptionproperty is added that is used by accessibility services to describe what the button does. Provide it whenever the button does not contain visible text explaining its action.
Create Custom Items#
For completely custom implementations, use EditorComponent.remember and render your custom UI inside decoration. To demonstrate the default values, all properties are assigned to their default values unless specified otherwise:
@Composablefun rememberCustomItem() = EditorComponent.remember { id = { EditorComponentId("my.package.dock.newCustomItem") } scope = { remember(this) { Dock.ItemScope(parentScope = this) } } modifier = { Modifier } visible = { true } enterTransition = { EnterTransition.None } exitTransition = { ExitTransition.None } decoration = { Box( modifier = Modifier .fillMaxHeight() .clickable { Toast .makeText(editorContext.activity, "Hello World Clicked!", Toast.LENGTH_SHORT) .show() }, ) { Text( modifier = Modifier.align(Alignment.Center), text = "Hello World", ) } }}Required and optional properties:
-
id- the id of the custom item. Note that it is highly recommended that every uniqueEditorComponenthas a unique id. By default it contains a random value. -
scope- scope of this component. Every new value will trigger recomposition of allScopedPropertys such asvisible,enterTransition,exitTransitionetc. Consider using Composeandroidx.compose.runtime.Stateobjects in the lambdas for granular recompositions over updating the scope, since scope change triggers full recomposition of the component. Ideally, scope should be updated when the parent scope (scope of the parent component) is updated and when you want to observe changes from theEngine. By default it is derived from the parent component scope. -
modifier- Jetpack Compose modifier of this component. By default empty modifier is applied. -
visible- whether the custom item should be visible. Default value is always true. -
enterTransition- transition of the custom item when it enters the parent composable. Default value is always no enter transition. -
exitTransition- transition of the custom item when it exits the parent composable. Default value is always no exit transition. -
decoration- render your custom item here. You are responsible for drawing the UI, handling clicks, and applying any custom styling. Default value is always no decoration.
List of Available Dock.Buttons#
As you often saw in the previous sections, there are composable functions that look like this: Dock.Button.remember{name}. All these functions return a Dock.Button, they are public and can be used in your application. Note that all the properties of these functions have default values, therefore, you do not need to provide any values, however, if you want to modify any of the properties, it is exactly the same as described in Create New Buttons section.
| Button | ID | Description |
|---|---|---|
Dock.Button.rememberElementsLibrary | Dock.Button.Id.elementsLibrary | Opens library sheet with elements via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberOverlaysLibrary | Dock.Button.Id.overlaysLibrary | Opens library sheet with overlays via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberImagesLibrary | Dock.Button.Id.imagesLibrary | Opens library sheet with images via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberTextLibrary | Dock.Button.Id.textLibrary | Opens library sheet with text via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberShapesLibrary | Dock.Button.Id.shapesLibrary | Opens library sheet with shapes via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberStickersLibrary | Dock.Button.Id.stickersLibrary | Opens library sheet with stickers via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberAudiosLibrary | Dock.Button.Id.audiosLibrary | Opens library sheet with audios via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library. |
Dock.Button.rememberSystemGallery | Dock.Button.Id.systemGallery | Opens the system gallery via EditorEvent.LaunchContract. |
Dock.Button.rememberSystemCamera | Dock.Button.Id.systemCamera | Opens the system camera via EditorEvent.LaunchContract. |
Dock.Button.rememberImglyCamera | Dock.Button.Id.imglyCamera | Opens the IMG.LY camera via EditorEvent.LaunchContract. Note that the button can be used only if ly.img:camera:<same version as editor> dependency is added in your build.gradle file. For more information, check the camera documentation. |
Dock.Button.rememberReorder | Dock.Button.Id.reorder | Opens reorder sheet via EditorEvent.Sheet.Open. |
Dock.Button.rememberAdjustments | Dock.Button.Id.adjustments | Opens adjustment sheet via EditorEvent.Sheet.Open. |
Dock.Button.rememberFilter | Dock.Button.Id.filter | Opens filter sheet via EditorEvent.Sheet.Open. |
Dock.Button.rememberEffect | Dock.Button.Id.effect | Opens effect sheet via EditorEvent.Sheet.Open. |
Dock.Button.rememberBlur | Dock.Button.Id.blur | Opens blur sheet via EditorEvent.Sheet.Open. |
Dock.Button.rememberCrop | Dock.Button.Id.crop | Opens crop sheet via EditorEvent.Sheet.Open. |
Dock.Button.rememberResizeAll | Dock.Button.Id.resizeAll | Opens resize sheet via EditorEvent.Sheet.Open. |