---
title: "UI customization sidebars | Nutrient Web SDK"
canonical_url: "https://www.nutrient.io/guides/web/user-interface/ui-customization/custom-sidebars/"
md_url: "https://www.nutrient.io/guides/web/user-interface/ui-customization/custom-sidebars.md"
last_updated: "2026-06-08T16:38:57.321Z"
description: "Build custom sidebars with the UI slots API, define sidebar modes, render your own panels, and toggle them with view state."
---

# Custom sidebars

Sidebars can be customized using the `sidebar` slot in the `ui` configuration. You can read more about slots in the [introduction](https://www.nutrient.io/guides/web/user-interface/ui-customization/introduction.md) guide.

The `sidebar` group supports two types of customization:

- **`sidebar.container`** — Customize or hide the sidebar container itself (the panel showing thumbnails, outline, annotations, bookmarks, signatures, attachments, and layers). Return `null` from `render` to completely remove the sidebar.

- **`sidebar.[yourId]`** — Add custom sidebar panels with dynamic keys, as described below.

## Customizing the sidebar container

Use `sidebar.container` to fully replace or hide the default sidebar:

```tsx

NutrientViewer.load({
  ui: {
    sidebar: {
      // Hide the entire default sidebar (thumbnails, outline, bookmarks, etc.)
      container: (getInstance, id) => ({ render: () => null }),
    },
  },
});

```

You can also replace it with a custom implementation:

```tsx

NutrientViewer.load({
  ui: {
    sidebar: {
      container: (getInstance, id) => ({
        render: () => {
          const panel = document.createElement("div");
          panel.style.padding = "16px";
          panel.textContent = "Custom sidebar container";
          return panel;
        },
      }),
    },
  },
});

```

## Adding a slot for a custom sidebar

Slots for sidebars can be added inside the `sidebar` object in the `ui` configuration:

```tsx

NutrientViewer.load({
  //... Your configuration.
  ui: {
    sidebar: {
      // Configuration for sidebar customization.
    },
  },
});

```

A custom sidebar slot can be represented by a unique string identifier. Similar to other slots, you can pass a function as a value that returns an object with a `render` method.

Consider a custom sidebar with the identifier `myCustomSidebar`:

```tsx

NutrientViewer.load({
  //... Your configuration.
  ui: {
    sidebar: {
      myCustomSidebar: (getInstance, id) => ({
        render: (params) => {
          const div = document.createElement("div");
          div.innerText = "This is my custom sidebar!";
          div.style.padding = "10px";
          div.style.color = "#ccc";

          return div;
        },
      }),
    },
  },
});

```

This takes care of rendering the custom sidebar. However, it’s necessary to tell the SDK to display the sidebar before it’s visible in the UI. This can be done using the `sidebarMode` view state.

### Sidebar mode

The [sidebar mode](https://www.nutrient.io/guides/web/customizing-the-interface/controlling-the-sidebar-via-api.md#sidebar-mode) view state property (`sidebarMode`) controls which sidebar is currently visible. In addition to the values for [default (built-in) sidebars](https://www.nutrient.io/api/web/modules/NutrientViewer.html#.SidebarMode), it also accepts string values corresponding to the identifiers of custom sidebar slots.

To display the custom sidebar in the UI, you can use `instance.setViewState()` and set `sidebarMode` to `myCustomSidebar`, which corresponds to the same identifier used in the configuration slot:

```tsx

NutrientViewer.load({
  //... Your configuration.
  ui: {
    sidebar: {
      myCustomSidebar: () => ({
        render: () => {
          const div = document.createElement("div");
          div.innerText = "This is my custom sidebar!";
          div.style.padding = "20px 32px";
          div.style.color = "#1A1414";

          return div;
        },
      }),
    },
  },
}).then((instance) => {
  // `myCustomSidebar` corresponds to the ID of the custom sidebar slot defined in the UI -> sidebar configuration.
  instance.setViewState((viewState) =>
    viewState.set("sidebarMode", "myCustomSidebar"),
  );
});

```

You’ll see the custom sidebar rendered in the UI after the SDK loads.

### Initial view state

The code above showed how to add a custom sidebar and display it programmatically using the view state.

In addition to triggering the custom sidebar with `setViewState`, you can also set the [initial view state](https://www.nutrient.io/api/web/NutrientViewer.Configuration.html#initialViewState) to open the custom sidebar when the viewer loads. This relies on the same `sidebarMode` view state property:

```tsx

NutrientViewer.load({
  //... Your configuration.
  initialViewState: new NutrientViewer.ViewState({
    // `myCustomSidebar` corresponds to the ID of the custom sidebar slot defined in the UI -> sidebar configuration.
    sidebarMode: "myCustomSidebar",
  }),
  ui: {
    sidebar: {
      myCustomSidebar: () => ({
        render: () => {
          const div = document.createElement("div");
          div.innerText = "This is my custom sidebar!";
          div.style.padding = "10px";
          div.style.color = "#ccc";

          return div;
        },
      }),
    },
  },
});

```

### Lifecycle methods

Similar to other UI slots, custom sidebars support both the `onMount` and `onUnmount` [lifecycle methods](https://www.nutrient.io/guides/web/user-interface/ui-customization/introduction.md#lifecycle-methods).

- `onMount` — Called when the custom sidebar is mounted. You can use this to perform any setup actions, such as adding event listeners and analytics events.

- `onUnmount` — Called when the custom sidebar is unmounted. You can use this to perform any cleanup actions, such as removing event listeners.

Below is an example of how you can use lifecycle methods:

```tsx

NutrientViewer.load({
  //... Your configuration.
  ui: {
    sidebar: {
      myCustomSidebar: (getInstance, id) => {
        const div = document.createElement("div");
        div.innerText = "This is my custom sidebar!";
        div.style.padding = "10px";
        div.style.color = "#ccc";

        return {
          render: () => div,
          onMount: (id) => {
            console.log(`Custom sidebar ${id} mounted`);
            // Perform any setup actions here.
          },
          onUnmount: (id) => {
            console.log(`Custom sidebar ${id} unmounted`);
            // Perform any cleanup actions here.
          },
        };
      },
    },
  },
});

```

## Adding a toolbar item

You may want to add a toolbar item in the viewer sidebar dropdown menu to trigger the custom sidebar.

You can use the [toolbar items API](https://www.nutrient.io/guides/web/user-interface/main-toolbar/create-a-new-tool.md) to add a custom toolbar item that will toggle your custom sidebar:

```tsx

NutrientViewer.load({
  //... Your configuration.
  ui: {
    sidebar: {
      myCustomSidebar: () => ({
        render: () => {
          const div = document.createElement("div");
          div.innerText = "This is my custom sidebar!";
          div.style.padding = "10px";
          div.style.color = "#ccc";

          return div;
        },
      }),
    },
  },
}).then((instance) => {
  // Define a custom toolbar item.
  const myCustomSidebarToolbarItem = {
    type: "custom",
    id: "myCustomSidebarToolbarItem",
    title: "My Custom Sidebar",
    dropdownGroup: "sidebar",
    icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="hotpink"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 7H13V9H11V7ZM11 11H13V17H11V11Z"></path></svg>',
    onPress: () => {
      instance.setViewState((viewState) =>
        viewState.set(
          "sidebarMode",
          viewState.sidebarMode === "myCustomSidebar"? null
            : "myCustomSidebar",
        ),
      );
    },
  };

  // Add the custom toolbar item to the existing toolbar items.
  instance.setToolbarItems([...NutrientViewer.defaultToolbarItems,
    myCustomSidebarToolbarItem,
  ]);
});

```

The toolbar item to toggle your custom sidebar will now be available in the sidebar dropdown menu.

### Selection state for toolbar item

The example above doesn’t provide a `selected` state for the custom toolbar item, so the toolbar item won’t appear selected when the custom sidebar is open, although it will work correctly.

It’s possible to add an event listener for the `viewState.change` [event](https://www.nutrient.io/api/web/classes/NutrientViewer.Instance.html#~ViewStateChangeEvent) to update the `selected` state of the toolbar item based on the current `sidebarMode`:

```tsx <!-- Lines inserted: [{range: "42-54"}] -->

NutrientViewer.load({
  //... Your configuration.
  ui: {
    sidebar: {
      myCustomSidebar: () => ({
        render: () => {
          const div = document.createElement("div");
          div.innerText = "This is my custom sidebar!";
          div.style.padding = "20px 32px";
          div.style.color = "#1A1414";

          return div;
        },
      }),
    },
  },
}).then((instance) => {
  // Define a custom toolbar item.
  const myCustomSidebarToolbarItem = {
    type: "custom",
    id: "myCustomSidebarToolbarItem",
    title: "My Custom Sidebar",
    dropdownGroup: "sidebar",
    icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#1A1414"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 7H13V9H11V7ZM11 11H13V17H11V11Z"></path></svg>',

    onPress: () => {
      instance.setViewState((viewState) =>
        viewState.set(
          "sidebarMode",
          viewState.sidebarMode === "myCustomSidebar"? null
            : "myCustomSidebar",
        ),
      );
    },
  };

  // Add the custom toolbar item to the existing toolbar items.
  instance.setToolbarItems([...NutrientViewer.defaultToolbarItems,
    myCustomSidebarToolbarItem,
  ]);

  // Update the selected state of the toolbar item based on the current `sidebarMode`.
  instance.addEventListener("viewState.change", (viewState, prevViewState) => {
    if (viewState.sidebarMode === prevViewState.sidebarMode) {
      return;
    }

    instance.setToolbarItems((items) =>
      items.map((item) =>
        item.id === "myCustomSidebarToolbarItem"? {...item, selected: viewState.sidebarMode === "myCustomSidebar" }
          : item,
      ),
    );
  });
});

```

The toolbar item will now appear selected when the custom sidebar is open.
---

## Related pages

- [Set UI customization configuration](/guides/web/user-interface/ui-customization/set-ui.md)
- [Building a comment thread UI with the customization API](/guides/web/user-interface/ui-customization/comment-thread-example.md)
- [Customizing the Nutrient Web SDK UI](/guides/web/user-interface/ui-customization/introduction.md)
- [Slot customization examples](/guides/web/user-interface/ui-customization/examples.md)
- [Building headless document UIs](/guides/web/user-interface/ui-customization/headless-ui.md)
- [Supported slots for UI customization](/guides/web/user-interface/ui-customization/supported-slots.md)

