Skip to content

Ordered and Unordered Lists

Table of Contents

Basic Usage

You can add ordered and unordered lists using RichTextState:

// Toggle ordered list.
richTextState.toggleOrderedList()

// Toggle unordered list.
richTextState.toggleUnorderedList()

You can get if the current selection is an ordered or unordered list, using RichTextState:

// Get if the current selection is an ordered list.
val isOrderedList = richTextState.isOrderedList

// Get if the current selection is an unordered list.
val isUnorderedList = richTextState.isUnorderedList

List Nesting and Levels

You can increase or decrease the nesting level of lists using RichTextState:

// Increase list level (nesting)
richTextState.increaseListLevel()

// Decrease list level (un-nesting)
richTextState.decreaseListLevel()

// Check if list level can be increased/decreased
val canIncrease = richTextState.canIncreaseListLevel
val canDecrease = richTextState.canDecreaseListLevel

List Style Types

Ordered Lists

You can customize the style of ordered lists using different formats:

// Set ordered list style type
richTextState.config.orderedListStyleType = OrderedListStyleType.Decimal     // 1, 2, 3, ...
richTextState.config.orderedListStyleType = OrderedListStyleType.LowerAlpha  // a, b, c, ...
richTextState.config.orderedListStyleType = OrderedListStyleType.UpperAlpha  // A, B, C, ...
richTextState.config.orderedListStyleType = OrderedListStyleType.LowerRoman  // i, ii, iii, ...
richTextState.config.orderedListStyleType = OrderedListStyleType.UpperRoman  // I, II, III, ...

// Use different styles for different nesting levels
richTextState.config.orderedListStyleType = OrderedListStyleType.Multiple(
    OrderedListStyleType.UpperAlpha,  // First level: A, B, C
    OrderedListStyleType.LowerAlpha,  // Second level: a, b, c
    OrderedListStyleType.Decimal      // Third level: 1, 2, 3
)

Unordered Lists

You can customize the style of unordered lists using different bullet types:

// Set unordered list style type
richTextState.config.unorderedListStyleType = UnorderedListStyleType.Disc    // •
richTextState.config.unorderedListStyleType = UnorderedListStyleType.Circle  // ◦
richTextState.config.unorderedListStyleType = UnorderedListStyleType.Square  // ▪

// Use custom markers for different nesting levels
richTextState.config.unorderedListStyleType = UnorderedListStyleType.from(
    "•",  // First level
    "◦",  // Second level
    "▪"   // Third level
)

Per-List Style Types

Note: This API is marked @ExperimentalRichTextApi and may change in a future release.

The config style types above are editor-wide presentation: every list follows them and they are not saved with the content. A single list can also carry a style type of its own, the way an <ol> or <ul> carries a list-style-type in HTML. Set it on the lists at the selection:

// The whole ordered list at the cursor becomes a, b, c, ...
richTextState.setOrderedListStyleType(OrderedListStyleType.LowerAlpha)

// The whole unordered list at the cursor uses square bullets
richTextState.setUnorderedListStyleType(UnorderedListStyleType.Square)

// Back to the config style
richTextState.setOrderedListStyleType(null)

The style applies to every item of the list each selected item belongs to: the items at its nesting level, up to the previous and next paragraph that is not a list item, is nested less deeply, or is a list of the other kind. A nested list is a list of its own and keeps or sets its style independently. New items created with Enter, with the typing shortcuts, or by toggling a paragraph right below the list inherit its style. Changing the config afterwards does not affect a list with a style type of its own. Each call is one undo step.

For a toolbar, read the style type of the list at the selection. It is null when the selection is not in a list of that kind, when the list follows the config, or when the selected items carry different style types:

val orderedStyle: OrderedListStyleType? = richTextState.currentOrderedListStyleType
val unorderedStyle: UnorderedListStyleType? = richTextState.currentUnorderedListStyleType

A per-list style type is part of the content: toHtml() writes it as style="list-style-type: ..." on the <ol> or <ul>, setHtml() reads it from the list tag or from an <li>, and the document model and JSON carry it too. Only the predefined types have a CSS keyword and round trip:

Style type CSS keyword
OrderedListStyleType.Decimal decimal
OrderedListStyleType.LowerAlpha lower-alpha (also read from lower-latin)
OrderedListStyleType.UpperAlpha upper-alpha (also read from upper-latin)
OrderedListStyleType.LowerRoman lower-roman
OrderedListStyleType.UpperRoman upper-roman
OrderedListStyleType.ArabicIndic arabic-indic
OrderedListStyleType.Arabic arabic-abjad
UnorderedListStyleType.Disc disc
UnorderedListStyleType.Circle circle
UnorderedListStyleType.Square square

A custom style type (an own OrderedListStyleType implementation, an OrderedListStyleType.Multiple, or UnorderedListStyleType.from(...) with other prefixes) renders in the editor but is not written to HTML, the document or JSON. An unknown keyword in HTML leaves the list following the config. Markdown has no list style types.

List Indentation

You can control the list indentation using RichTextState:

// Change list indentation (ordered and unordered).
richTextState.config.listIndent = 20

// Change only ordered list indentation.
richTextState.config.orderedListIndent = 20

// Change only unordered list indentation.
richTextState.config.unorderedListIndent = 20

List Prefix Alignment

Note: This API is marked @ExperimentalRichTextApi and may change in a future release.

You can control where the list marker (1., 10., •, ...) sits relative to the indent "gutter" using ListPrefixAlignment:

// Default: HTML-style - the marker sits inside the gutter and ends at the content
// start, so "1." and "10." have their dots aligned vertically.
richTextState.config.listPrefixAlignment = ListPrefixAlignment.End

// Alternative: every item's marker starts at the same left edge, giving a uniform
// left edge even when item widths differ (e.g. "1." vs "10.").
richTextState.config.listPrefixAlignment = ListPrefixAlignment.Start
Alignment Layout Example
End (default) marker right-aligned to content start, dots align vertically 1. / 10. / 11. - dots stacked
Start marker left-aligned to the indent origin, marker left edges stack 1. / 10. / 11. - numbers stacked

If the configured indent is smaller than the marker width (for example orderedListIndent = 0), End clamps the first-line indent at 0 so the marker stays visible. Clamping (rather than switching formulas per item) keeps the whole list on a single layout so paragraphs with different marker widths stay visually consistent.

List Marker Style

Note: This API is marked @ExperimentalRichTextApi and may change in a future release.

You can control how list markers (•, 1., etc.) inherit styles from the list item's text using ListMarkerStyleBehavior:

// Default: marker inherits typography from the item's text but drops decorations.
richTextState.config.listMarkerStyleBehavior = ListMarkerStyleBehavior.InheritFromText

// Alternative: every marker renders with the default span style regardless of content.
richTextState.config.listMarkerStyleBehavior = ListMarkerStyleBehavior.AlwaysDefault

With InheritFromText (the default), markers match the paragraph's typography so they stay visually aligned with the text. This matches Google Docs behavior:

Attribute Applied to marker
color yes
fontSize yes
fontWeight (bold) yes
fontStyle (italic) yes
fontFamily yes
letterSpacing yes
textDecoration (underline, strikethrough) no
background (highlight) no
baselineShift (super/subscript) no
shadow no
textGeometricTransform no

Use AlwaysDefault when you want bullets that look identical no matter how the line is formatted (for example, editors that style markers entirely through a separate theme).

Common Operations

Styling the markers directly

To style the bullets and numbers on their own, set listMarkerStyle. It is applied on top of the marker style behavior above, so anything it leaves unset still follows that behavior:

@OptIn(ExperimentalRichTextApi::class)
richTextState.config.listMarkerStyle = SpanStyle(color = Color.Red)

// Bold, colored numbers and bullets
richTextState.config.listMarkerStyle = SpanStyle(color = Color.Gray, fontWeight = FontWeight.Bold)

It is an appearance setting of the editor, like the link and code span colors: it applies to every list in the editor and is not saved in the document, so toHtml() and toMarkdown() do not include it. Reset it with SpanStyle().

Default Values

By default, the Rich Text Editor uses these configurations: - Ordered List Style: OrderedListStyleType.Multiple with: - First level: Decimal (1, 2, 3, ...) - Second level: LowerRoman (i, ii, iii, ...) - Third level: LowerAlpha (a, b, c, ...) - Unordered List Style: UnorderedListStyleType.from with: - First level: • (bullet) - Second level: ◦ (circle) - Third level: ▪ (square) - List Indentation: 38 - List Prefix Alignment: ListPrefixAlignment.End (HTML-style, dots aligned) - List Marker Style: ListMarkerStyleBehavior.InheritFromText - Exit List on Empty Item: true (configurable via richTextState.config.exitListOnEmptyItem) - List Typing Shortcuts: true (configurable via richTextState.config.listTypingShortcutsEnabled)

Keyboard Shortcuts

The editor supports common keyboard shortcuts for list operations: - Tab: Increase list level (indent) - Shift + Tab: Decrease list level (outdent) - Enter on an empty list item: Exit the list - Backspace at the start of a list item: Decrease list level or exit list

Typing Shortcuts

Typing a list marker at the start of a paragraph turns it into a list: - - or *: unordered list - A number followed by . (for example 1. or 57.): ordered list starting at that number

To keep lists but turn these shortcuts off, so the typed characters stay as text:

richTextState.config.listTypingShortcutsEnabled = false

The list functions (toggleOrderedList, toggleUnorderedList and the others) and lists in loaded or pasted content keep working. To remove lists from the editor altogether, leave them out of the feature set instead (see Editor features).

List Behavior

When working with lists: 1. Creating a new list item: - Press Enter at the end of a list item to create a new one - The new item inherits the same level and style as the previous one

  1. Exiting a list:
  2. Press Enter on an empty list item (configurable via exitListOnEmptyItem)
  3. Press Backspace at the start of an empty list item:
    • When list level > 1: Decreases the list level by 1
    • When list level = 1: Exits the list

Note: The behavior of pressing Enter on an empty list item can be configured using richTextState.config.exitListOnEmptyItem. When set to true (default), it exits the list. When set to false, it creates a new list item.

  1. Converting between list types:
  2. You can convert between ordered and unordered lists by toggling the respective type
  3. The list level and content are preserved during conversion

Visual Examples

Note: The following examples are representations of how lists might appear. The actual appearance in your application may vary based on your configuration settings (style type, indentation, etc.).

Ordered List Example

1. First level item
   a. Second level item
      i. Third level item
   b. Another second level
2. Back to first level

Unordered List Example

• First level item
  ◦ Second level item
    ▪ Third level item
  ◦ Another second level
• Back to first level