---
title: "Jetpack Compose contextual toolbar for PDF viewer | Nutrient Android SDK"
canonical_url: "https://www.nutrient.io/guides/android/jetpack-compose/components/contextual-toolbar/"
md_url: "https://www.nutrient.io/guides/android/jetpack-compose/components/contextual-toolbar.md"
last_updated: "2026-10-12T10:12:58.189Z"
description: "Render the Nutrient annotation, document editing, and content editing toolbars yourself with NutrientContextualToolbar: close button slot, shapes for the top and side docks, colors, drag handle, and docking."
---

# Contextual toolbar

`NutrientContextualToolbar` renders the contextual toolbar that’s currently shown: the annotation creation toolbar, the document editing toolbar, or the content editing toolbar. One composable covers all three because they share the same structure. Each bar has a close button, a row of grouped items with submenus, and a drag handle. The bar docks at the top of the window or at one of its sides. Like the [main toolbar](https://www.nutrient.io/guides/android/jetpack-compose/components/main-toolbar.md), it takes a slot for the close button, which a host can replace, and plain parameters for the chrome. The defaults for both live in `NutrientContextualToolbarDefaults`.

The bar reads its state from the [`ToolbarCoordinator`](https://www.nutrient.io/api/android/nutrient/com.pspdfkit.compose.toolbar.adapter/-toolbar-coordinator/index.html). On every mode change, the SDK writes the bar’s kind, items, dock, and draggability to `coordinator.contextualToolbar`. It does this whether or not the SDK renders the bar itself.

## Basic usage

Turn off the SDK’s own contextual toolbar and compose the bar over the document. The composable shows itself when the user enters a mode and hides when the user leaves it. While no bar is shown, it draws nothing:

```kotlin

val configuration = PdfActivityConfiguration.Builder(context).defaultToolbarEnabled(false).defaultContextualToolbarEnabled(false).build()
val documentState = rememberDocumentState(uri, configuration)
val coordinator by documentState.toolbarCoordinator.collectAsState()

Box(Modifier.fillMaxSize()) {
    DocumentView(documentState = documentState, modifier = Modifier.fillMaxSize())
    coordinator?.let { c ->
        NutrientMainToolbar(coordinator = c, modifier = Modifier.fillMaxWidth())
        NutrientContextualToolbar(coordinator = c, modifier = Modifier.align(Alignment.TopCenter).fillMaxWidth())
    }
}

```

`defaultContextualToolbarEnabled(false)` works independently of `defaultToolbarEnabled(false)`. You can keep the SDK’s main toolbar and render only the contextual bar yourself, or do the reverse. When the SDK’s contextual toolbar is disabled, `PdfActivity` also stops insetting the document for a bar it no longer draws.

## Items, clicks, and grouping

The composable doesn’t take items, click handlers, or grouping as parameters. Instead, change the bar’s items with `coordinator.updateContextualItems`, and react to taps with `coordinator.addActionListener`. Intercept taps before the SDK handles them with `addActionInterceptor`, and control grouping with `setContextualToolbarGroupingProvider`. The [contextual toolbars](https://www.nutrient.io/guides/android/customizing-the-interface/customizing-the-toolbar.md) guide covers each of these. Its instructions work unchanged for a bar that the host renders, because both bars read the same coordinator. When no grouping provider is registered on the coordinator, `defaultGroupingProvider` on the composable supplies a grouping other than the SDK’s presets.

## The close button slot

`closeButton` is the first item in the bar. Its default is `NutrientContextualToolbarCloseButton()`. The slot runs inside a `NutrientContextualToolbarScope`, which exposes the bar’s `state` and a `dispatch` function. `dispatch` tags each action with this bar’s kind:

```kotlin

NutrientContextualToolbar(
    coordinator = c,
    closeButton = {
        IconButton(onClick = { dispatch(ToolbarAction.ItemClicked(R.id.pspdf__toolbar_close_button)) }) {
            Icon(Icons.Rounded.Close, contentDescription = "Done", tint = UiTheme.colors.mainToolbar.iconColor)
        }
    },
)

```

The stock button dispatches `ItemClicked` with `R.id.pspdf__toolbar_close_button`, and the SDK leaves the mode in response. The slot can read `state.position` to see which dock the bar is in for a button that should look different on a side dock.

## Shapes for two docks

The bar looks different at the top than at a side, so it takes two shape and border pairs. `shape` and `border` apply when the bar is docked at the top. `sideDockedShape` and `sideDockedBorder` apply when it’s docked at the start or end edge, where it renders as a vertical rail:

```kotlin

NutrientContextualToolbar(
    coordinator = c,
    modifier = Modifier.align(Alignment.BottomCenter).padding(16.dp),
    shape = RoundedCornerShape(28.dp),
    sideDockedShape = RoundedCornerShape(28.dp),
    shadowElevation = 8.dp,
    colors = NutrientContextualToolbarDefaults.colors().copy(
        backgroundColor = MaterialTheme.colorScheme.primaryContainer,
        textColor = MaterialTheme.colorScheme.onPrimaryContainer,
    ),
    itemIndication = ToolbarItemIndication(shape = CircleShape),
)

```

`colors` is a `MainToolbarColors`, the same type the main toolbar uses. The bar applies it to the SDK’s buttons, the submenu sheet, and a custom close button. The default reads the `pspdf__contextualToolbarStyle` theme attributes through the surrounding [`NutrientTheme`](https://www.nutrient.io/api/android/nutrient/com.pspdfkit.compose.theme/-nutrient-theme.html).

## Width and layout

A top-docked bar is only as wide as its items unless the modifier sets a width. With `Modifier.fillMaxWidth()`, the bar becomes the edge-to-edge strip that `PdfActivity` shows. With no width modifier, it renders as a pill. A side-docked bar is always `NutrientContextualToolbarDefaults.ToolbarSize` thick (58dp) and as tall as its items.

Along its main axis, the bar lays out the close button first, then the grouped items, then the drag handle. The bar divides its available length into 48dp slots to decide how many items fit, and it passes that capacity to the grouping provider. A narrower bar therefore folds more items into submenus. Long-pressing a group slides out its submenu sheet on the document side of the bar. The composable reserves a 58dp strip on that side for the sheet.

The bar reserves `windowInsets` inside its surface, so its background extends behind the insets. The default is no insets, because `NutrientToolbarScaffold` and the SDK’s own mount already place the bar inside them. If a host places the bar edge to edge itself, it should pass `WindowInsets.statusBars` or a similar value.

## Docking and dragging

The composable renders where the host places it, using the dock that `coordinator.contextualToolbar.value.position` reports. Moving the bar between docks is the host’s job:

- Pass a `dragListener` to show the drag handle. The `NutrientContextualToolbarDragListener` receives `onDragStarted()`, `onDragged(deltaX, deltaY)`, and `onDragEnded()`. The host moves the bar during the drag. On release, it calls `coordinator.setContextualToolbarPosition(...)` with the dock the bar landed on. A bar that doesn’t support docking passes no listener and shows no handle.

- `effectivePosition` overrides the dock the bar lays out for. Use it when the host can’t honor the `position` in the state — for example, when a vertical bar doesn’t fit.

- `verticalDockAvailable` tells the bar whether a drag may end on a side dock. When it returns `false`, the bar offers only the top dock while dragging.

To get the same docking chrome as `PdfActivity`, place the bar through [`NutrientToolbarScaffold` or `NutrientToolbars`](https://www.nutrient.io/guides/android/jetpack-compose/components/toolbar-hosting/) instead of positioning it yourself. That chrome includes drop zones and dock slots. The scaffold supplies `effectivePosition` and `dragListener` through its `ContextualToolbarSlotScope`.

## API

This is the full signature of `NutrientContextualToolbar`. The table after it describes each parameter and its default:

```kotlin

@Composable
fun NutrientContextualToolbar(
    coordinator: ToolbarCoordinator,
    modifier: Modifier = Modifier,
    defaultGroupingProvider: ContextualToolbarGroupingProvider? = null,
    dragListener: NutrientContextualToolbarDragListener? = null,
    verticalDockAvailable: () -> Boolean = { true },
    effectivePosition: ToolbarPosition? = null,
    closeButton: @Composable NutrientContextualToolbarScope.() -> Unit = { NutrientContextualToolbarCloseButton() },
    shape: Shape = NutrientContextualToolbarDefaults.Shape,
    border: BorderStroke? = null,
    sideDockedShape: Shape = NutrientContextualToolbarDefaults.SideDockedShape,
    sideDockedBorder: BorderStroke? = NutrientContextualToolbarDefaults.sideDockedBorder(),
    shadowElevation: Dp = NutrientContextualToolbarDefaults.shadowElevation,
    windowInsets: WindowInsets = NutrientContextualToolbarDefaults.NoWindowInsets,
    colors: MainToolbarColors = NutrientContextualToolbarDefaults.colors(),
    itemIndication: ToolbarItemIndication = NutrientContextualToolbarDefaults.ItemIndication,
    enter: EnterTransition = NutrientContextualToolbarDefaults.enterTransition,
    exit: ExitTransition = NutrientContextualToolbarDefaults.exitTransition,
)

```

| Parameter                             | Default                                  | What it does                                                                            |
| ------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------- |
| `coordinator`                         |                                          | The [`ToolbarCoordinator`](https://www.nutrient.io/api/android/nutrient/com.pspdfkit.compose.toolbar.adapter/-toolbar-coordinator/index.html) whose `contextualToolbar` state the bar renders.           |
| `modifier`                            | `Modifier`                               | Placement and width of the bar.                                                         |
| `defaultGroupingProvider`             | `null`                                   | Grouping used when none is registered on the coordinator; `null` keeps the SDK presets. |
| `dragListener`                        | `null`                                   | Receives drag gestures from the handle; `null` hides the handle.                        |
| `verticalDockAvailable`               | `{ true }`                               | Whether a drag may end on a side dock; `false` offers only the top dock.                |
| `effectivePosition`                   | `null`                                   | Dock to lay out for when it differs from the state’s `position`.                        |
| `closeButton`                         | `NutrientContextualToolbarCloseButton()` | Leading slot in `NutrientContextualToolbarScope` (`state`, `dispatch`).                 |
| `shape`, `border`                     | `RectangleShape`, `null`                 | Chrome when docked at the top.                                                          |
| `sideDockedShape`, `sideDockedBorder` | 16dp rounded, 1dp themed border          | Chrome when docked at a side.                                                           |
| `shadowElevation`                     | themed                                   | Elevation of the surface.                                                               |
| `windowInsets`                        | `NoWindowInsets`                         | Insets reserved inside the surface.                                                     |
| `colors`                              | `colors()`                               | `MainToolbarColors` for the bar, its items, and the submenu sheet.                      |
| `itemIndication`                      | `ItemIndication`                         | Shape and ripple of item states.                                                        |
| `enter`, `exit`                       | fade in, fade out                        | Transitions when a bar appears or disappears.                                           |

`NutrientContextualToolbarDefaults` also exposes `ToolbarSize`, `toolbarSize(context)` (the themed `pspdf__contextualToolbarHeight`), `VerticalMargin`, `HorizontalMargin`, `DropZoneCornerRadius`, `dragTargetColor(context)`, and `statusBarColor(context)` for hosts that build their own docking chrome.

## Behavior

- Long-pressing an icon-only item shows a tooltip on the document side of the bar. Long-pressing a group opens its submenu.

- The bar’s surface is tagged `pspdf__contextual_toolbar`. Each item carries its resource entry name as a test tag — for example, `pspdf__annotation_toolbar_item_undo`.

- The bar supports right-to-left layouts: `ToolbarPosition.Start` is the right edge there.

## Related

- [Contextual toolbars](https://www.nutrient.io/guides/android/customizing-the-interface/customizing-the-toolbar.md) — Items, clicks, grouping, and docking through the coordinator.

- [Toolbar hosting](https://www.nutrient.io/guides/android/jetpack-compose/components/toolbar-hosting.md) — `NutrientToolbars` and `NutrientToolbarScaffold`, which place this bar with the SDK’s docking chrome.

- [Floating toolbars](https://www.nutrient.io/guides/android/jetpack-compose/components/floating-toolbars.md) — `NutrientFloatingContextualToolbar`, the same bar as a floating pill.

- Catalog app — `CustomToolbarsDocumentViewExample`, `CustomToolbarsPdfActivityExample`.
---

## Related pages

- [Floating toolbars](/guides/android/jetpack-compose/components/floating-toolbars.md)
- [Main toolbar](/guides/android/jetpack-compose/components/main-toolbar.md)
- [Tab bar](/guides/android/jetpack-compose/components/tab-bar.md)
- [Toolbar hosting](/guides/android/jetpack-compose/components/toolbar-hosting.md)

