---
title: "Headless text markup | Nutrient Web SDK"
canonical_url: "https://www.nutrient.io/guides/web/headless/text-markup/"
md_url: "https://www.nutrient.io/guides/web/headless/text-markup.md"
last_updated: "2026-05-15T19:10:05.084Z"
description: "Create highlights, underlines, strikeouts, and squigglies from the user’s text selection without the default Nutrient Web SDK markup toolbar."
---

# Headless text markup

The `instance.annotations.textMarkup` namespace lets you create text markup annotations, highlights, underlines, strikeouts, and squigglies without going through the default text selection toolbar. Use it from a custom inline toolbar, a keyboard shortcut your application defines, or any other UI you want to layer over the viewer.

Looking for the visual side of this? See the [supported slots reference](https://www.nutrient.io/guides/web/user-interface/ui-customization/supported-slots.md) for replacing the inline toolbar that appears when the user selects text. This page covers the programmatic surface you’ll call from inside that custom UI.

## When to use this

Reach for the headless text markup API when you’re building:

- A custom inline toolbar that floats near the text selection with your own styling.

- A keyboard shortcut your application defines that highlights the current selection.

- A “highlight all matches” workflow that combines [`search`](https://www.nutrient.io/api/web/classes/NutrientViewer.Instance.html#startuisearch) results with `createFromCurrentTextSelection`.

- A custom action driven by a remote command from a multiuser collaboration layer.

## API reference

| Method                                                                 | Notes                                                                                                                                                                                                         |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `instance.annotations.textMarkup.createFromCurrentTextSelection(type)` | Creates markup annotations of `type` over the current text selection. `type` is one of `'highlight'`, `'underline'`, `'strikeout'`, `'squiggly'`. Throws if no text is currently selected.                    |
| `instance.annotations.textMarkup.enterHighlighterMode(options?)`       | Enters continuous text-highlighter mode (`text-highlighter`) so subsequent text selections are highlighted automatically. Pass `{ presetPatch }` to merge values into the highlight preset before activation. |
| `instance.annotations.textMarkup.getColorPresets()`                    | Returns the resolved color presets for markup annotations, separated by `highlight` and `markup`.                                                                                                             |

`createFromCurrentTextSelection` returns a list of the created annotations. Because it produces one annotation per page that the selection spans, the list can contain more than one entry.

## Example: A custom inline markup toolbar

This example renders a minimal floating toolbar above the user’s text selection. Each button creates a different markup type for the selected text:

```js

const instance = await NutrientViewer.load({
  container: "#viewer",

  document: "contract.pdf"
});

const toolbar = document.getElementById("markup-toolbar");

instance.addEventListener("textSelection.change", (selection) => {
  // The event emits either a `TextSelection` or `null`. A null check is enough.
  if (!selection) {
    toolbar.style.display = "none";
    return;
  }

  toolbar.style.display = "flex";
});

function markup(type) {
  return () => {
    instance.annotations.textMarkup.createFromCurrentTextSelection(type);
  };
}

toolbar.querySelector("#highlight").onclick = markup("highlight");

toolbar.querySelector("#underline").onclick = markup("underline");

toolbar.querySelector("#strikeout").onclick = markup("strikeout");

toolbar.querySelector("#squiggly").onclick = markup("squiggly");

```

The created annotations follow the current preset for the chosen type. To create them with a specific color or opacity, update the highlight preset first and then create from the selection. `setCurrentAnnotationPreset` only switches which preset is active. To change a preset’s value, use `setAnnotationPresets` with the callback form so you merge into the existing preset map:

```js

instance.setAnnotationPresets((presets) => ({...presets,
  highlight: {...presets.highlight,
    color: NutrientViewer.Color.fromHex("#FFEB3B")

  }
}));
instance.setCurrentAnnotationPreset("highlight");

instance.annotations.textMarkup.createFromCurrentTextSelection("highlight");

```

## Example: Matching the default color presets

Render a color picker that reuses the swatches from the default markup toolbar. `getColorPresets()` returns two entries — use `highlight` for the highlight preset, and `markup` for the underline, strikeout, and squiggly presets:

```js

const presets = instance.annotations.textMarkup.getColorPresets();

const palette = document.getElementById("markup-color-palette");
presets.highlight.presets.forEach((preset) => {
  const swatch = document.createElement("button");
  swatch.style.backgroundColor = preset.color.toHex();
  swatch.onclick = () => {
    instance.setAnnotationPresets((presets) => ({...presets,
      highlight: {...presets.highlight, color: preset.color }
    }));
    instance.setCurrentAnnotationPreset("highlight");
  };
  palette.appendChild(swatch);
});

```

See the [color presets](https://www.nutrient.io/guides/web/headless/color-presets.md) guide for the full cross-namespace pattern.

## Related

- [Color presets](https://www.nutrient.io/guides/web/headless/color-presets.md) — The cross-namespace pattern for reading preset colors.

- [Redactions from selection](https://www.nutrient.io/guides/web/headless/redactions-from-selection.md) — The same selection-based pattern for redactions.
---

## Related pages

- [Headless callout annotations](/guides/web/headless/callout.md)
- [Headless color presets](/guides/web/headless/color-presets.md)
- [Headless image annotations](/guides/web/headless/image.md)
- [Annotation clipboard](/guides/web/headless/clipboard.md)
- [Headless](/guides/web/headless.md)
- [Headless ink annotations](/guides/web/headless/ink.md)
- [Headless stamp annotations](/guides/web/headless/stamp.md)
- [Headless link annotations](/guides/web/headless/link.md)
- [Headless note annotations](/guides/web/headless/note.md)
- [Redactions from text selection](/guides/web/headless/redactions-from-selection.md)
- [Programmatic notes panel](/guides/web/headless/notes-panel.md)
- [Headless text annotations](/guides/web/headless/text-annotations.md)
- [Headless shape annotations](/guides/web/headless/shape.md)

