23. Tables

Tables are one of the most intricate, yet refined areas of the AsciiDoc syntax. Armed with a bit of knowledge, you should discover that they are both easy to create and easy to read in raw form. Yet, under all that simplicity, they are remarkably sophisticated.

Tables are delimited by |=== and made up of cells. The default table data format is PSV (Prefix Separated Values), which means that the processor creates a new cell each time it encounters a vertical bar (|). Cells are grouped into rows. Each row must share the same number of cells, taking into account any column or row spans. Then, each consecutive cell in a row is placed in a separate column.

The simple table example below consists of two columns and three rows.

Simple table
|=== (1)
(2)
| Cell in column 1, row 1 | Cell in column 2, row 1  (3)
(4)
| Cell in column 1, row 2 | Cell in column 2, row 2

| Cell in column 1, row 3 | Cell in column 2, row 3

|=== (1)
1 The table’s content boundaries are defined by a vertical bar followed by three equal signs (|===).
2 Inserting a blank line before the first row is a trick to ensure the first row is not treated as the table header.
3 The new cell is marked by a vertical bar (|).
4 Rows can optionally be separated by any number of blank lines.
Result: Rendered simple table

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 1, row 3

Cell in column 2, row 3

Like with all blocks, you can add a role to a table using the role attribute. The role attribute becomes a CSS class when converted to HTML. The preferred shorthand for assigning the role attribute is to put the role name in the first position of the block attribute list prefixed with a . character, as shown here:

[.rolename]
|===

| Cell in column 1, row 1 | Cell in column 2, row 1 | Cell in column 3, row 1

| Cell in column 1, row 2 | Cell in column 2, row 2 | Cell in column 3, row 2

|===

Leading and trailing spaces around cell content is stripped and, therefore, don’t affect the table’s layout when rendered. The two examples below illustrate how leading and trailing spaces don’t change the rendered table’s layout.

Cell content adjacent to the |
|===

|Cell in column 1, row 1|Cell in column 2, row 1

|===
Result: Rendered table when cell content was entered adjacent to the |

Cell in column 1, row 1

Cell in column 2, row 1

Cell content with varying leading and trailing spaces
|===

| Cell in column 1, row 1        |          Cell in column 2, row 1

|===
Result: Rendered table when cell content was bounded by varying leading and trailing spaces

Cell in column 1, row 1

Cell in column 2, row 1

There are multiple ways to group cells into a row. The cells in a row can be placed on:

  1. the same line

  2. consecutive, individual lines

  3. a combination of a and b

Cells on the same line
|===

|Cell in column 1, row 1 |Cell in column 2, row 1 |Cell in column 3, row 1

|Cell in column 1, row 2 |Cell in column 2, row 2 |Cell in column 3, row 2

|===
Result: Rendered table when multiple cells where entered on the same line

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

When the cells of a row are individually entered on consecutive lines, the cols attribute is needed to specify the number of columns in the table. If the cols attribute is not set, the first non-blank line inside the block delimiter (|===) determines the number of columns.

Cells on consecutive, individual lines
[cols="3*"] (1)
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
1 The cols attribute states that this table has three columns. The * is a repeat operator which is explained in the column specifiers section.
Result: Rendered table when cells where listed on consecutive, individual lines

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Rows can be formed from adjacent lines of individual cells and cells listed on the same line.

Cells on the same line and individual lines
[cols="3*"]
|===
|Cell in column 1, row 1 |Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2 |Cell in column 3, row 2
|===
Result: Cells on the same line and individual lines

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The next sections describe and demonstrate the variety of ways you can customize table cells, rows and columns.

23.1. Columns

The number of columns in a table is determined by the number of cells found in the first non-blank line after the table delimiter (|===) or by the values assigned to the cols attribute.

For example, the syntax in the two examples below will both converted to a table with two columns.

|===

|Cell in column 1, row 1 |Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2

|===
Result: Rendered table with two columns as defined by the number of cells in the first row

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

[cols="2*"]
|===

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2

|===
Result: Rendered table with two columns as defined by the cols attribute

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

When a single number is assigned to the cols attribute, its value indicates the number of columns. Each column will be the same width. However, the number of columns can also be assigned as a comma delimited list. The number of entries in the list determines the number of columns.

The comma delimited list below creates a table with four columns of equal width.

[cols="1,1,1,1"]

This syntax provides that same result:

[cols="4*"]

Now, let’s talk about that asterisk in the syntax above.

23.2. Column Formatting

The AsciiDoc syntax provides a variety of ways to control the size, style and layout of content within columns. These specifiers can be applied to whole columns.

To apply a specifier to a column, you must set the cols attribute and assign it a value. A column specifier can contain any of the following components:

  • multiplier

  • align

  • width

  • style

Each component is optional.

The multiplier operator (*) is used when you want a specifier to apply to more than one consecutive column. If used, the multiplier must always be placed at the beginning of the specifier.

For example:

[cols="3*"] (1)
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
1 The table will consist of three columns, as indicated by the 3. The * operator ensures that the default layout and style will be applied to all of the columns.
Result: Rendered table with multiplier applied

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The alignment component allows you to horizontally or vertically align a column’s content. Content can be horizontally aligned left (<), center (^), or right (>).

To horizontally center the content in all of the columns, add the ^ operator after the multiplier.

[cols="3*^"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with horizontal, center alignment applied to all columns

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

What if you only want to center the content in the last column? Assign the default styles to the preceding columns, and ^ to the last column in a comma separated list.

[cols="2*,^"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with horizontal, center alignment applied to last column

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Let’s specify a different horizontal alignment for each column.

[cols="<,^,>"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with a different horizontal alignment for each column

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

You’ll notice that the content in the examples above is only centered on the horizontal. It can also be aligned vertically when the alignment operator is prefixed with a dot (.). Content can be vertically aligned to the top (<), middle (^), or bottom (>) of a cell.

To vertically align the content to the middle of the cells in all of the columns, add a . and then the ^ operator after the multiplier.

[cols="3*.^"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with vertical, middle alignment applied to all columns

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

If you only want to align the content to the bottom of each cell in the last column, you’d assign the default styles to the preceding columns, and > to the last column in a comma separated list.

[cols="2*,.>"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with vertical, bottom alignment applied to last column

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Let’s specify a different vertical alignment for each column.

[cols=".<,.^,.>"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with a different vertical alignment for each column

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Finally, we’ll also horizontally center the content in the last column.

[cols=".<,.^,^.>"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Rendered table with a different vertical alignment for each column and horizontal, center alignment in the last column

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

When both a horizontal and vertical alignment is assigned to a column, the horizontal alignment operator must precede the vertical operator.

The width component sets the width of a column. Its value can be a proportional integer (the default is 1) or a percentage (1 to 99).

[cols="1,2,6"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with column sizes adjusted by a proportional integer

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

When assigning percentage values to the cols attribute, you do not need to include the percent sign (%).

[cols="50,20,30"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with column sizes adjusted by a percentage

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Let’s create a table with custom widths and alignments.

[cols=".<2,.^5,^.>3"]
|===
|Cell in column 1, row 1 with lots and lots and lots and lots of content
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2 and another bucket of content, and then a jelly roll of content
|===
Result: Rendered table with variable widths and alignments

Cell in column 1, row 1 with lots and lots and lots and lots of content

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2 and another bucket of content, and then a jelly roll of content

The style component must always be located at the end of the specifier. When no style name is provided column contents will be processed as regular inline text.

The column styles are described in the table below.

Style Name Value Description

AsciiDoc

a

Supports block-level elements (paragraphs, delimited blocks, and block macros). This style effectively creates a nested, standalone AsciiDoc document. Implicit attributes such as doctitle from the parent document will be shadowed. Custom attributes are inherited.

Emphasis

e

Text is italicized

Header

h

Header styles are applied to the column

Literal

l

Column content is treated as if it were inside a literal block

Monospaced

m

Text is rendered in monospaced font

None (default style)

d

Text is handled like a normal paragraph. Supports all markup (i.e., inline formatting, inline macros) that is permitted in a paragraph.

Strong

s

Text is bolded

Verse

v

Column content is treated as if it were inside a verse block

Let’s apply the header style to the first column, the monospaced style to the second, the strong style to the third, and the emphasis style to the fourth.

[cols="h,m,s,e"]
|===
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1
|Cell in column 4, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|Cell in column 4, row 2
|===
Result: Rendered table with a header, monospaced, and strong styled column

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 4, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Cell in column 4, row 2

Specifiers can also be applied to individual cells.

23.3. Cell Formatting

In addition to sharing many of the column specifier capabilities, cell specifiers allow cells to span rows and columns. Like a column specifier, a cell specifier is made up of components. These components, listed and defined below, are all optional.

  • span

  • align

  • style

A cell specifier is prefixed directly to the cell delimiter (|) preceding the content you want to customize.

The span component can duplicate a cell or have it span multiple rows or columns.

To duplicate a cell in multiple, consecutive columns, prefix the | with the multiplication factor and the * operator.

Cell duplicated across three columns
|===

|Cell in column 1, row 1 |Cell in column 2, row 1 |Cell in column 3, row 1

3*|Same cell content in columns 1, 2, and 3

|Cell in column 1, row 3
|Cell in column 2, row 3
|Cell in column 3, row 3

|===
Result: Rendered table where cell was duplicated across three columns

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Same cell content in columns 1, 2, and 3

Same cell content in columns 1, 2, and 3

Same cell content in columns 1, 2, and 3

Cell in column 1, row 3

Cell in column 2, row 3

Cell in column 3, row 3

To have a cell span multiple, consecutive columns, prefix the | with the span factor and the + operator.

Cell spanning three columns
|===

|Cell in column 1, row 1 |Cell in column 2, row 1 |Cell in column 3, row 1

3+|Content in a single cell that spans columns 1, 2, and 3

|Cell in column 1, row 3
|Cell in column 2, row 3
|Cell in column 3, row 3

|===
Result: Rendered table where cell spans three columns

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Content in a single cell that spans columns 1, 2, and 3

Cell in column 1, row 3

Cell in column 2, row 3

Cell in column 3, row 3

If you want to have a cell span multiple, consecutive rows, prefix the span factor with a dot (.).

Cell spanning two rows
|===

|Cell in column 1, row 1 |Cell in column 2, row 1 |Cell in column 3, row 1

.2+|Content in a single cell that spans rows 2 and 3
|Cell in column 2, row 2
|Cell in column 3, row 2

|Cell in column 2, row 3
|Cell in column 3, row 3

|===
Result: Rendered table where a cell spans two rows

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Content in a single cell that spans rows 2 and 3

Cell in column 2, row 2

Cell in column 3, row 2

Cell in column 2, row 3

Cell in column 3, row 3

Of course you can combine spanning over columns and rows. The number before the dot (.) is the number of columns to span and the number after the dot (.) is the number of rows to span.

Cell spanning columns and rows
|===

|Column 1, row 1 |Column 2, row 1 |Column 3, row 1 |Column 4, row 1

|Column 1, row 2
2.3+|Content in a single cell that spans over rows and columns
|Column 4, row 2

|Column 1, row 3
|Column 4, row 3

|Column 1, row 4
|Column 4, row 4
|===
Result: Rendered table where a cell spans over columns and rows

Column 1, row 1

Column 2, row 1

Column 3, row 1

Column 4, row 1

Column 1, row 2

Content in a single cell that spans over rows and columns

Column 4, row 2

Column 1, row 3

Column 4, row 3

Column 1, row 4

Column 4, row 4

The alignment component for cells works the same as the column specifier alignment component.

Cells aligned horizontally, vertically, and across a span of three columns
[cols="3"]
|===
^|Prefix the `{vbar}` with `{caret}` to center content horizontally
<|Prefix the `{vbar}` with `<` to align the content to the left horizontally
>|Prefix the `{vbar}` with `>` to align the content to the right horizontally

.^|Prefix the `{vbar}` with a `.` and `{caret}` to center the content in the cell vertically
.<|Prefix the `{vbar}` with a `.` and `<` to align the content to the top of the cell
.>|Prefix the `{vbar}` with a `.` and `>` to align the content to the bottom of the cell

3+^.^|This content spans three columns (`3{plus}`) and is centered horizontally (`{caret}`) and vertically (`.{caret}`) within the cell.

|===
Result: Rendered cells aligned horizontally, vertically, and across a span of three columns

Prefix the | with ^ to center content horizontally

Prefix the | with < to align the content to the left horizontally

Prefix the | with > to align the content to the right horizontally

Prefix the | with a . and ^ to center the content in the cell vertically

Prefix the | with a . and < to align the content to the top of the cell

Prefix the | with a . and > to align the content to the bottom of the cell

This content spans three columns (3+) and is centered horizontally (^) and vertically (.^) within the cell.

To use a pipe (|) within the content of a cell without creating a new cell, you must use the {vbar} attribute.

The style component can also be applied to individual cells. For example, you can apply the AsciiDoc element styles to an individual cell by prefixing the vertical bar with an a.

Comparing cells using AsciiDoc styles and no AsciiDoc styles
[cols="2"]
|===

a|This cell is prefixed with an `a`, so the processor interprets the following lines as an AsciiDoc list.

* List item 1
* List item 2
* List item 3
|This cell *is not* prefixed with an `a`, so the processor does not interpret the following lines as an AsciiDoc list.

* List item 1
* List item 2
* List item 3

a|This cell is prefixed with an `a`, so the processor honors the `lead` style on the following paragraph.

[.lead]
I am a paragraph styled with the lead attribute.
|This cell *is not* prefixed with an `a`, so the processor does not honor the `lead` style on the following paragraph.

[.lead]
I am a paragraph styled with the lead attribute.
|===
Result: Rendered table comparing cells using AsciiDoc styles and no AsciiDoc styles

This cell is prefixed with an a, so the processor interprets the following lines as an AsciiDoc list.

  • List item 1

  • List item 2

  • List item 3

This cell is not prefixed with an a, so the processor does not interpret the following lines as an AsciiDoc list.

* List item 1 * List item 2 * List item 3

This cell is prefixed with an a, so the processor honors the lead style on the following paragraph.

I am a paragraph styled with the lead attribute.

This cell is not prefixed with an a, so the processor does not honor the lead style on the following paragraph.

[.lead] I am a paragraph styled with the lead attribute.

Source code listing can be placed inside cells by using the listing syntax.

Cells with source code listing
|===
|Source Code 1 |Source Code 2

a|
[source,python]
----
import os
print "%s" %(os.uname())
----

a|
[source,python]
----
import os
print ("%s" %(os.uname()))
----
|===
Result: Rendered table with cells containing source code listing
Source Code 1 Source Code 2
import os
print "%s" %(os.uname())
import os
print ("%s" %(os.uname()))

For the grand finale, let’s apply a variety of specifiers to individual cells.

Building a variety of cell specifiers
|===

2*>m|This content is duplicated across two columns.

It is aligned right horizontally.

And it is monospaced.

.3+^.>s|This cell spans 3 rows. The content is centered horizontally, aligned to the bottom of the cell, and strong.
e|This content is emphasized.

.^l|This content is aligned to the top of the cell and literal.

v|This cell contains a verse
that may one day expound on the
wonders of tables in an
epic sonnet.

|===
Result: Rendered table featuring a variety of cell specifiers

This content is duplicated across two columns.

It is aligned right horizontally.

And it is monospaced.

This content is duplicated across two columns.

It is aligned right horizontally.

And it is monospaced.

This cell spans 3 rows. The content is centered horizontally, aligned to the bottom of the cell, and strong.

This content is emphasized.

This content is aligned to the top of the cell and literal.

This cell contains a verse that may one day expound on the wonders of tables in an epic sonnet.

23.4. Header Row

The first row of a table is promoted to a header row if the header option is set (either explicitly or implicitly).

You can enable the header option by adding the header keyword to the comma-separated list of values in the options attribute on the table. You can also enable the header option by adding the %header directive to the table style (the first positional attribute).

In the example below, the table has an attribute list containing an options attribute that includes the header option.

Table with the header value assigned to the options attribute
[cols=2*,options="header"]
|===
|Name of Column 1
|Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===
Result: Rendered table when the header value is assigned to the options attribute
Name of Column 1 Name of Column 2

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Alternately, you can define the header row based on how you layout the table. Asciidoctor use the following conventions to determine when the first row should become the header row:

  1. The first line of content inside the table block delimiters is not empty.

  2. The second line of content inside the table block delimiters is empty.

  3. The options attribute has not been specified in the block attributes (prior to 1.5.5).

As seen in the result of the example below, if all of these rules hold, then the first row of the table is treated as a header.

Table that has an implicit header row
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===
Result: Rendered table when the header row was assigned implicitly
Name of Column 1 Name of Column 2

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

If you want to suppress this implicit behavior of promoting the first row to a header row, set the option value noheader (or add the %noheader directive to the table style).

Notice that when the implicit method of assigning the header row is used, it’s not necessary to set the cols attribute. That’s because the number of columns are determined by the number of cells in the first line if the cols attribute is absent.

We’re considering using a similar convention for enabling the footer in the future. Thus, if you rely on this convention to enable the header row, it’s advised that you not put all the cells in the last row on the same line unless you intend on making it the footer row.

The last row of a table can be styled as a footer by adding the footer keyword to the options attribute.

[options="footer"]
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2

|Footer in column 1, row 3
|Footer in column 2, row 3
|===
Result: Table rendered with a footer
Name of Column 1 Name of Column 2

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Footer in column 1, row 3

Footer in column 2, row 3

23.6. Table Width

By default, a table will span the width of the content area. To constrain the width of the table to a fixed value, set the width attribute in the table’s attribute list. The width is an integer percentage value ranging from 1 to 100. The % sign is optional.

Table with width set to 75%
[width=75%]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with a width of 75%
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

Alternately, you can make the width fit the content by setting the autowidth option. The columns inherit this setting, so individual columns will also be sized according to the content.

Table using autowidth
[%autowidth]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: The width of the table is sized to accomodate the automatic column widths
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

If you want each column to have an automatic width, but want the table to span the width of the content area, add the stretch role to the table or set the width attribute to 100%.

Full-width table with autowidth columns
[%autowidth.stretch]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Columns are sized to the content, but table spans the width of the page
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The autowidth option is not recognized by the DocBook converter.

If you want to apply autowidth only to certain columns, use the special value ~ as the width of the column. In this case, width values are assumed to be a percentage value (i.e., 100-based).

Table with both fixed and autowidth columns
[cols="25h,~,~"]
|===
|small |as big as the column needs to be |the rest
|===

23.7. Table Borders

The borders on a table are controlled using the frame and grid attributes. You can combine these two attributes to achieve a variety of border styles for your tables.

23.7.1. Frame

The border around a table is controlled using the frame attribute. By default, the frame attribute is assigned the all value, which draws a border on each side of the table. If you set the frame attribute, you can override the default value with the values topbot, sides or none.

The topbot value draws a border on the top and bottom of the table.

[frame=topbot]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with frame=topbot
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The sides value draws a border on the right and left side of the table.

[frame=sides]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with frame=sides
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The none value removes the borders around the table.

[frame=none]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with frame=none
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

23.7.2. Grid

The borders between the cells in a table are controlled using the grid attribute. By default, the grid attribute is assigned the all value, which draws a border between all cells. If you set the grid attribute, you can override the default value with the values rows, cols or none.

The rows value draws a border between the rows of the table.

[grid=rows]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with grid=rows
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The cols value draws borders between the columns.

[grid=cols]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with grid=cols
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

The none value removes all borders between the cells.

[grid=none]
|===
|Name of Column 1 |Name of Column 2 |Name of Column 3

|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 3, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|===
Result: Table rendered with grid=none
Name of Column 1 Name of Column 2 Name of Column 3

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 3, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Cell in column 3, row 2

23.8. Striping

Starting with Asciidoctor 2.0, the rows in the body of a table are not shaded. You can configure the rows to be striped (as in zebra stripes) using an attribute. Use the stripes table attribute to configure the stripes for a single table. Use the table-stripes document attribute to configure stripes for all tables.

Striping is done by adding a background color to the specified rows. The stripes attribute accepts the following values:

  • none - no rows are shaded (default in Asciidoctor >= 2.0)

  • even - even rows are shaded (default in Asciidoctor < 2.0)

  • odd - odd rows are shaded

  • all - all rows are shaded

  • hover - the row under the mouse cursor is shaded (HTML only)

In the following example, the stripes are enabled for even rows in the table body (the row that contains A2, B2, and C2).

[cols=3*, stripes=even]
|===
| A1
| B1
| C1

| A2
| B2
| C2

| A3
| B3
| C3
|===

Under the covers, the stripes attribute applies the CSS class stripes-<value> (e.g., stripes-none) to the table tag. As a shorthand, you can simply apply the CSS class to the table directly using a role.

[.stripes-even,cols=3*]
|===
| A1
| B1
| C1

| A2
| B2
| C2

| A3
| B3
| C3
|===

If you want to apply stripes to all tables in the document, set the table-stripes attribute in the document header. You can still override this setting per table.

:table-stripes: even

[cols=3*]
|===
| A1
| B1
| C1

| A2
| B2
| C2

| A3
| B3
| C3
|===

In the HTML output, table striping is done using CSS and thus depends on the stylesheet to supply the necessary styles. The default stylesheet for Asciidoctor includes these styles.

23.9. Orientation

A table can be displayed in landscape (rotated 90 degrees counterclockwise) using the rotate option (preferred):

[%rotate]
|===
|a |b
|c |d
|===

or the orientation attribute:

[orientation=landscape]
|===
|a |b
|c |d
|===

Currently, this is only implemented by the DocBook backend (it sets the attribute orient="land").

23.10. Nested Tables

Table cells marked with the AsciiDoc table style (a) support nested tables in addition to normal block content. To distinguish the inner table from the enclosing one, you need to use !=== as the table delimiter and a cell separator that differs from that used for the enclosing table. The default cell separator for a nested table is !, though you can choose another character by defining the separator attribute on the table.

Although nested tables are not technically valid in DocBook 5.0, the DocBook toolchain processes them anyway.

The following example contains a nested table in the last cell. Notice the nested table has its own format, independent of that of the outer table:

[cols="1,2a"]
|===
| Col 1 | Col 2

| Cell 1.1
| Cell 1.2

| Cell 2.1
| Cell 2.2

[cols="2,1"]
!===
! Col1 ! Col2

! C11
! C12

!===

|===
Result: A nested table
Col 1 Col 2

Cell 1.1

Cell 1.2

Cell 2.1

Cell 2.2

Col1 Col2

C11

C12

We recommend using nested tables sparingly. There’s usually a better way to present the information.

23.11. Table Caption

If you specify a title on a table, the title will be used as part of the table’s caption. Adding a title also designates the table as a formal table.

By default, the title of a formal table is prefixed with the label Table <n>. followed by a space, where <n> is the 1-based index of all formal tables in the document.

.A formal table
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===
Table 1. A table with a title
Name of Column 1 Name of Column 2

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

You can customize the caption label by specifying the caption attribute. (Don’t let the name of the attribute mislead you. The caption attribute only sets the caption’s label, not the whole caption line).

If you want a space between the label and the title, you must add a trailing space to the value of the caption attribute.
[caption="Table A. "]
.A formal table
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===
Table A. A formal table
Name of Column 1 Name of Column 2

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

If you want the caption of the table to only consist of the caption label, use the following syntax:

[caption=,title='{table-caption} {counter:table-number}']
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===

Alternately, you can write is as follows:

.{blank}
[caption='{table-caption} {counter:table-number}']
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===

If you want to exclude the caption label altogether, simply assign a blank value to the caption attribute.

[caption=]
.A formal table
|===
|Name of Column 1 |Name of Column 2

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===
A formal table
Name of Column 1 Name of Column 2

Cell in column 1, row 1

Cell in column 2, row 1

Cell in column 1, row 2

Cell in column 2, row 2

Alternatively, you can disable the caption label for tables globally by undefining the table-caption document attribute.

:table-caption!:

23.12. Escaping the Cell Separator

The parser scans for the cell separator to partition cells before it processes the cell text. So even if you try to hide the cell separator using an inline passthrough, the parser will see it. If the cell contain contains the cell separator, you must escape that character. There are three ways to escape it:

  • Prefix the character with a leading backslash (i.e., \|), which will be removed from the output.

  • Use the {vbar} attribute reference as a substitute.

  • Change the cell separator used by the table.

Unless you do one of these things, the cell separator will be interpreted as a cell boundary.

Consider the following example, which escapes the cell separator using a leading backslash:

[cols=2*]
|====
|The default separator in PSV tables is the \| character.
|The \| character is often referred to as a "`pipe`".
|====

This table will render as follows:

Result: Converted PSV table that contains pipe characters

The default separator in PSV tables is the | character.

The | character is often referred to as a “pipe”.

Notice that the pipe character appears without the leading backslash (i.e., unescaped) in the rendered result.

An alternative is to use the {vbar} attribute reference as a substitute. This approach produces the same result as the previous example.

[cols=2*]
|====
|The default separator in PSV tables is the {vbar} character.
|The {vbar} character is often referred to as a "`pipe`".
|====

Escaping each cell separator character that appears in the content of a cell can be tedious. There are also times when you can’t or don’t want to modify the cell content (perhaps because it is being included from another file). To address these cases, AsciiDoc allows you to override the cell separator.

The cell separator is controlled using the separator attribute on the table block. You’ll want to select a character that will never be used for content. A good candidate is the broken bar, or ¦.

Here’s the previous example rewritten using a custom separator.

[cols=2*,separator=¦]
|====
¦The default separator in PSV tables is the | character.
¦The | character is often referred to as a "`pipe`".
|====

Notice that it’s no longer necessary to escape the pipe character in the content of the table cells. You can safely use the original cell separator in the cell content and not worry about it being interpreted as the boundary of a cell.

23.13. Delimiter-Separated Values

Tables can also be populated from data formatted as delimiter-separated values (i.e., data tables). In contrast with the PSV format, in which the delimiter is placed in front of each cell value, the delimiter in a delimiter-separated format (CSV, TSV, DSV) is placed between the cell values (called a separator) and does not accept a cell formatting spec. Each line of data is assumed to represent a single row, though you’ll learn that’s not a strict rule. How the table data gets interpreted is controlled by the format and separator attributes on the table.

What the delimiter?

Aren’t comma-separated values a subset of delimiter-separated values? It really depends on who you consult.

The term “delimiter-separated values” in this text refers to the family of data formats that use a delimiter, including comma-separated values (CSV), tab-separated values (TSV) and delimited data (DSV), all of which are supported in AsciiDoc tables. CSV is the data format used most often.

“Comma-separated values” is really a misleading term since CSV can use delimiters other than , as the field separator (which, in this context, separates cells). What we’re really talking about is how the data is interpreted.

CSV and TSV both use a delimiter and an optional enclosing character, loosely based on RFC 4180. DSV (i.e., delimited data) only uses a delimiter, which can be escaped using a backslash; an enclosing character is not recognized. These parsing rules are described in detail in Data Table Formats.

Let’s consider an example of using comma-separated values (CSV) to populate an AsciiDoc table with data. To instruct the processor to read the data as CSV, set the value of the format attribute on the table to csv. When the format attribute is set to csv, the default data separator is a comma (,), as seen in the table below.

[%header,format=csv]
|===
Artist,Track,Genre
Baauer,Harlem Shake,Hip Hop
The Lumineers,Ho Hey,Folk Rock
|===
Result: Rendered CSV table
Artist Track Genre

Baauer

Harlem Shake

Hip Hop

The Lumineers

Ho Hey

Folk Rock

This feature is particularly useful when you want to populate a table in your manuscript from data stored in a separate file. You can do so using the include directive between the table delimiters, as shown here:

[%header,format=csv]
|===
include::tracks.csv[]
|===

If your data is separated by tabs instead of commas, set the format to tsv (tab-separated values) instead.

Now let’s consider an example of using delimited data (DSV) to populate an AsciiDoc table with data. To instruct the processor to read the data as DSV, set the value of the format attribute on the table to dsv. When the format attribute is set to dsv, the default data separator is a colon (:), as seen in the table below.

[%header,format=dsv]
|===
Artist:Track:Genre
Robyn:Indestructable:Dance
The Piano Guys:Code Name Vivaldi:Classical
|===
Result: Rendered DSV table
Artist Track Genre

Robyn

Indestructable

Dance

The Piano Guys

Code Name Vivaldi

Classical

23.13.1. Data Table Formats

The CSV and TSV data formats are parsed differently from the DSV data format. The following two sections outline those differences.

CSV and TSV

Table data in either CSV or TSV format is parsed according to the following rules, loosely based on RFC 4180:

  • The default delimiter for CSV is a comma (,) while the default delimiter for TSV is a tab character.

  • Blank lines are skipped (unless enclosed in a quoted value).

  • Whitespace surrounding each value is stripped.

  • Values can be enclosed in double quotes (").

    • A quoted value may contain zero or more separator or newline characters.

    • A newline begins a new row unless the newline is enclosed in double quotes.

    • A quoted value may include the double quote character if escaped using another double quote ("").

    • Newlines in quoted values are retained (as of 1.5.7).

  • If rows do not have the same number of cells (“ragged” tables), cells are shuffled to fully fill the rows.

    • This is different behavior than Excel, which pads short rows with empty cells.

    • Extra cells at the end of the last row get dropped.

    • As a rule of thumb, data for a single row should be on the same line.

DSV

Table data in DSV format is parsed according to the following rules:

  • The default delimiter for DSV is a colon (:).

  • Blank lines are skipped.

  • Whitespace surrounding each value is stripped.

  • The delimiter character can be included in the value if escaped using a single backslash (\:).

  • If rows do not have the same number of cells (“ragged” tables), cells are shuffled to fully fill the rows.

23.13.2. Custom Delimiters

Each data format has a default separator associated with it (csv = comma, tsv = tab, dsv = colon), but the separator can be changed to any character (or even a string of characters) by setting the separator attribute on the table.

Here’s an example of a DSV table that uses a custom separator character (i.e., delimiter):

A DSV table with a custom separator
[format=dsv,separator=;]
|===
a;b;c
d;e;f
|===
To make a TSV table, you can set the format attribute to csv and the separator to \t. Though the tsv format is preferred.

The separator is independent of the processing rules for the format. If you set format=dsv and separator=,, the data will be processed using the DSV rules, even though the data looks like CSV.

23.13.3. Shorthand Notation for Data Tables

Asciidoctor provides shorthand notation for specifying the data format of a table. The first position of the table block delimiter (i.e., |===) can be replaced by a built-in delimiter to set the table format (e.g., ,=== for CSV).

To make a CSV table, you can use ,=== as the table block delimiter:

,===
Artist,Track,Genre

Baauer,Harlem Shake,Hip Hop
,===
Result: Rendered CSV table using shorthand syntax
Artist Track Genre

Baauer

Harlem Shake

Hip Hop

To make a DSV table, you can use :=== as the table block delimiter:

:===
Artist:Track:Genre

Robyn:Indestructable:Dance
:===
Result: Rendered DSV table using shorthand syntax
Artist Track Genre

Robyn

Indestructable

Dance

When using either the CSV or DSV shorthand, you do not need to set the format attribute as it’s implied.

To make a TSV table, you can set the format attribute to tsv instead of having to set the format to csv and the separator to \t. In this case, you can use either |=== or ,=== as the table block delimiter. There is no special delimited block notation for a TSV table.

23.13.4. Formatting Cells in a Data Table

The delimited formats do not provide a way to express formatting of individual table cells. Instead, you can apply cell formatting to all cells in a given column using the cols spec on the table:

[format=csv,cols="1h,1a"]
|===
Sky,image::sky.jpg[]
Forest,image::forest.jpg[]
|===

Data tables do not support cells that span multiple rows or columns, since that information can only be expressed at the cell level. You are advised to use the PSV format if you need that functionality.

23.14. Summary

Table attributes and values
Attribute Description Value Description Notes

cols

comma-separated list of column specifiers

specifiers

see Columns for details

format

data format of the table’s contents

psv

cells are delimited by separator (default |) (aka prefix-separated values)

dsv

cells are delimited by a colon (:) (aka delimiter-separated values)

csv

cells are delimited by a comma (,) (aka comma-separated values)

tsv

cells are delimited by a tab character (aka tab-separated values)

separator

character used to separate cells

|

default for top-level tables

!

default for nested tables

user-defined

any sequence of literal characters (e.g., ¦ or ][). Ideally a pattern not found in the cell content.

frame

draws a border around the table

all

border on all sides (default)

topbot

border on top and bottom

none

no borders

sides

border on left and right sides

grid

draws boundary lines between rows and columns

all

draws boundary lines around each cell (default)

cols

draws boundary lines between columns

rows

draws boundary lines between rows

none

no boundary lines

stripes

controls row shading (via background color)

none

no rows are shaded

even

even rows are shaded (default in Asciidoctor < 2.0)

odd

odd rows are shaded

hover

row under the mouse cursor is shaded (HTML only)

all

all rows are shaded

align

horizontally aligns table within page width

left

aligns to left side of page (default)

Not yet implemented in Asciidoctor. Applies to HTML output only. The align and float attributes are mutually exclusive.

right

aligns to right side of page

center

horizontally aligns to center of page

float

floats the table to the specified side of the page

left

floats the table to the left side of the page (default)

Applies to HTML output only. Must be used in conjunction with the table’s width attribute to take effect. The float and align attributes are mutually exclusive.

right

floats the table to the right side of the page

halign

horizontally aligns all of the cell contents in a table

left

aligns the contents of the cells to the left (default)

Not implemented in Asciidoctor. Define instead using column or cell specifiers (e.g., 3*>), which take precedence over this value.

right

aligns the contents of the cells to the right

center

aligns the contents to the cell centers

valign

vertically aligns all of the cell contents in a table

top

aligns the cell contents to the top of the cell (default)

Define instead using column or cell specifiers (e.g., 3*.>), which take precedence over this value.

bottom

aligns the cell contents to the bottom of the cell

middle

aligns the cell contents to the middle of the cell

orientation

rotates the table

landscape

rotated 90 degrees counterclockwise

Equivalent to setting the rotate option, which is preferred. DocBook only.

options

comma separated list of option names

header

promotes first row to the table header

header and footer rows are omitted by default

footer

promotes last row to the table footer

breakable

allows the table to split across a page (default)

Mutually exclusive. DocBook only (specifically for generating PDF output).

unbreakable

prevents the table from being split across a page

autowidth

disables explicit column widths (ignores cols attribute)

rotate

Prints the table in landscape

Equivalent to setting the orientation to landscape. DocBook only.

width

the table width relative to the available page width

user defined value

a percentage value between 0% and 100%