More powerful and customizable tables in Typst.
NOTE: Please open an issue if you find a bug with tablex and I'll get to it as soon as I can. (PRs are also welcome!)
If you'd like to appear here, consider sponsoring the project!
A large amount of tablex's features have successfully been upstreamed by this package's author to Typst's built-in table
and grid
elements (see the new Tables Guide, at https://typst.app/docs/guides/table-guide/, and the table
element's reference, at https://typst.app/docs/reference/model/table/, for more information).
This means that, starting with Typst 0.11.0, many advanced table features can now be used with Typst grids and tables without tablex! This includes:
table.cell(inset: ..., align: ..., fill: ...)[body]
, and #show table.cell: it => ...
instead of map-cells
);table.cell(colspan: 2, rowspan: 2)[body]
);stroke
parameter of table.cell
to control the lines around it, and you can use table.hline
and table.vline
which work similarly to their tablex counterparts - the equivalent of map-hlines
and map-vlines
is table(stroke: (x, y) => (left: ..., right: ..., top: ..., bottom: ...))
);table.header(... cells ...)
);grid
as well by replacing table
with grid
where applicable (e.g. grid.cell
instead of table.cell
).Additionally, built-in Typst tables have support for features which weren't previously available within tablex, such as repeatable table footers (through table.footer
and grid.footer
).
Therefore, for the vast majority of use cases, you will no longer need to use this library. However:
fit-spans
option, through which colspans and rowspans don't cause auto
-sized columns and/or rows to expand, is not yet supported in built-in tables.If there any questions, feel free to open a thread in the Discussions
page of this repository, or ping the author on Discord. Thanks to everyone who supported me throughout tablex's development and the upstreaming process. I hope you enjoy the new update, and have fun with tables! 😄
And make sure to keep an eye for future tablex updates. 😉
To use this library through the Typst package manager (for Typst v0.6.0+), write for example #import "@preview/tablex:0.0.8": tablex, cellx
at the top of your Typst file (you may also add whichever other functions you use from the library to that import list!).
For older Typst versions, download the file tablex.typ
from the latest release (or directly from the main branch, for the 'bleeding edge') at the tablex repository (https://github.com/PgBiel/typst-tablex) and place it on the same folder as your own Typst file. Then, at the top of your file, write for example #import "tablex.typ": tablex, cellx
(plus whichever other functions you use from the library).
This library should be compatible with Typst v0.2.0, v0.3.0, v0.4.0, v0.5.0, v0.6.0, v0.7.0, v0.8.0, v0.9.0 and v0.10.0. Using the latest Typst version is always recommended in order to make use of the latest optimizations and features available.
Here's an example of what tablex
can do:
Here's the code for that table:
#import "@preview/tablex:0.0.8": tablex, rowspanx, colspanx
#tablex(
columns: 4,
align: center + horizon,
auto-vlines: false,
// indicate the first two rows are the header
// (in case we need to eventually
// enable repeating the header across pages)
header-rows: 2,
// color the last column's cells
// based on the written number
map-cells: cell => {
if cell.x == 3 and cell.y > 1 {
cell.content = {
let value = int(cell.content.text)
let text-color = if value < 10 {
red.lighten(30%)
} else if value < 15 {
yellow.darken(13%)
} else {
green
}
set text(text-color)
strong(cell.content)
}
}
cell
},
/* --- header --- */
rowspanx(2)[*Username*], colspanx(2)[*Data*], (), rowspanx(2)[*Score*],
(), [*Location*], [*Height*], (),
/* -------------- */
[John], [Second St.], [180 cm], [5],
[Wally], [Third Av.], [160 cm], [10],
[Jason], [Some St.], [150 cm], [15],
[Robert], [123 Av.], [190 cm], [20],
[Other], [Unknown St.], [170 cm], [25],
)
#table
(Update: tablex's syntax was designed to be compatible with Typst tables created up to Typst v0.10.0. The new table features introduced in Typst v0.11.0 use syntax which isn't compatible with tablex, so it won't be a drop-in replacement in that case. However, tablex does have its own syntax for those features, as will be explained below!)
In most cases, you should be able to replace #table
with #tablex
and be good to go for a start - it should look very similar (if not identical). Indeed, the syntax is very similar for the basics:
#import "@preview/tablex:0.0.8": tablex
#tablex(
columns: (auto, 1em, 1fr, 1fr), // 4 columns
rows: auto, // at least 1 row of auto size
fill: red,
align: center + horizon,
stroke: green,
[a], [b], [c], [d],
[e], [f], [g], [h],
[i], [j], [k], [l]
)
There are still a few oddities in the library (see Known Issues for more info), but, for the vast majority of cases, replacing #tablex
by #table
should work just fine. (Sometimes you can even replace #grid
by #gridx
- see the line customization section for more -, but not always, as the behavior is a bit different.)
This is mostly a word of caution in case anything I haven't anticipated happens, but, based on my tests (and after tons of bug-fixing commits), the vast majority of tables (that don't face one of the listed known issues) should work just fine under the library.
Note: If your document is written in a right-to-left (RTL) script, you may wish to enable rtl: true
for your tables so that the order of cells and lines properly follows your text direction (when combined with set text(dir: rtl)
). This is necessary because tablex cannot detect that setting automatically at the moment (while the native Typst table can and flips itself horizontally automatically). See the tablex option reference for more information.
Your cells can now span more than one column and/or row at once, with colspanx
/ rowspanx
:
#import "@preview/tablex:0.0.8": tablex, colspanx, rowspanx
#tablex(
columns: 3,
colspanx(2)[a], (), [b],
[c], rowspanx(2)[d], [ed],
[f], (), [g]
)
Note that the empty parentheses there are just for organization, and are ignored (unless they come before the first cell - more on that later). They're useful to help us keep track of which cell positions are being used up by the spans, because, if we try to add an actual cell at these spots, it will just push the others forward, which might seem unexpected.
Use colspanx(2, rowspanx(2)[d])
to colspan and rowspan at the same time. Be careful not to attempt to overwrite other cells' spans, as you will get a nasty error.
Note (since tablex v0.0.8): By default, colspans and rowspans can cause spanned auto
columns and rows to expand to fit their contents (only the last spanned track - column or row - can expand). If you'd like colspans to not affect column sizes at all (and thus "fit" within their spanned columns), you may specify fit-spans: (x: true)
to the table. Similarly, you can specify fit-spans: (y: true)
to have rowspans not affect row sizes at all. To apply both effects, use either fit-spans: true
or fit-spans: (x: true, y: true)
. You can also apply this to a single colspan (for example) with colspanx(2, fit-spans: (x: true))[a]
, as this option is available not only for the whole table but also for each cell. See the reference section for more information.
You can now ensure the first row (or, rather, the rows covered by the first rowspan) in your table repeats across pages. Just use repeat-header: true
(default is false
).
Note that you may wish to customize this. Use repeat-header: 6
to repeat for 6 more pages. Use repeat-header: (2, 4)
to repeat only on the 2nd and the 4th page (where the 1st page is the one the table starts in). Additionally, use header-rows: 5
to ensure the first (e.g.) 5 rows are part of the header (by default, this is 1 - more rows will be repeated where necessary if rowspans are used).
Also, note that, by default, the horizontal lines below the header are transported to other pages, which may be an annoyance if you customize lines too much (see below). Use header-hlines-have-priority: false
to ensure that the first row in each page will dictate the appearance of the horizontal lines above it (and not the header).
Note: Depending on the size of your document, repeatable headers might not behave properly due to certain limitations in Typst's introspection system (as observed in https://github.com/PgBiel/typst-tablex/issues/43).
Example:
#import "@preview/tablex:0.0.8": tablex, hlinex, vlinex, colspanx, rowspanx
#pagebreak()
#v(80%)
#tablex(
columns: 4,
align: center + horizon,
auto-vlines: false,
repeat-header: true,
/* --- header --- */
rowspanx(2)[*Names*], colspanx(2)[*Properties*], (), rowspanx(2)[*Creators*],
(), [*Type*], [*Size*], (),
/* -------------- */
[Machine], [Steel], [5 $"cm"^3$], [John p& Kate],
[Frog], [Animal], [6 $"cm"^3$], [Robert],
[Frog], [Animal], [6 $"cm"^3$], [Robert],
[Frog], [Animal], [6 $"cm"^3$], [Robert],
[Frog], [Animal], [6 $"cm"^3$], [Robert],
[Frog], [Animal], [6 $"cm"^3$], [Robert],
[Frog], [Animal], [6 $"cm"^3$], [Robert],
[Frog], [Animal], [6 $"cm"^3$], [Rodbert],
)
Every single line in the table is either a hlinex
(horizontal) or vlinex
(vertical) instance. By default, there is one between every column and between every row - unless you specify a custom line for some column or row, in which case the automatic line for it will be removed (to allow you to freely customize it). To disable this behavior, use auto-lines: false
, which will remove all automatic lines. You may also remove only automatic horizontal lines with auto-hlines: false
, and only vertical with auto-vlines: false
.
Note: gridx
is an alias for tablex
with auto-lines: false
.
For your custom lines, write hlinex()
at any position and it will add a horizontal line below the current cell row (or at the top, if before any cell). You can use hlinex(start: a, end: b)
to control the cells which that line spans (a
is the first column number and b
is the last column number). You can also specify its stroke (color/thickness) with hlinex(stroke: red + 5pt)
for example. To position it at an arbitrary row, use hlinex(y: 6)
or similar. (Columns and rows are indexed starting from 0.)
Something similar occurs for vlinex()
, which has start
, end
(first row and last row it spans), and also stroke
. They will, by default, be placed to the right of the current cell, and will span the entire table (top to bottom). To override the default placement, use vlinex(x: 2)
or similar.
Note: Only one hline or vline with the same span (same start/end) can be placed at once.
Note: You can also place vlines before the first cell, in which case they will be placed consecutively, each after the last vlinex()
. That is, if you place several of them in a row (before the first cell only), then it will not place all of them at one location (which is normally what happens if you try to place multiple vlines at once), but rather one after the other. With this behavior, you can also specify ()
between each vline to skip certain positions (again, only before the first cell - afterwards, all ()
are ignored). Note that you can also just ignore this entirely and use vlinex(x: 0)
, vlinex(x: 1)
, ..., vlinex(x: columns.len())
for full control.
Here's some sample usage:
#import "@preview/tablex:0.0.8": tablex, gridx, hlinex, vlinex, colspanx, rowspanx
#tablex(
columns: 4,
auto-lines: false,
// skip a column here vv
vlinex(), vlinex(), vlinex(), (), vlinex(),
colspanx(2)[a], (), [b], [J],
[c], rowspanx(2)[d], [e], [K],
[f], (), [g], [L],
// ^^ '()' after the first cell are 100% ignored
)
#tablex(
columns: 4,
auto-vlines: false,
colspanx(2)[a], (), [b], [J],
[c], rowspanx(2)[d], [e], [K],
[f], (), [g], [L],
)
#gridx(
columns: 4,
(), (), vlinex(end: 2),
hlinex(stroke: yellow + 2pt),
colspanx(2)[a], (), [b], [J],
hlinex(start: 0, end: 1, stroke: yellow + 2pt),
hlinex(start: 1, end: 2, stroke: green + 2pt),
hlinex(start: 2, end: 3, stroke: red + 2pt),
hlinex(start: 3, end: 4, stroke: blue.lighten(50%) + 2pt),
[c], rowspanx(2)[d], [e], [K],
hlinex(start: 2),
[f], (), [g], [L],
)
You can also bulk-customize lines by specifying map-hlines: h => new_hline
and map-vlines: v => new_vline
. This includes any automatically generated lines. For example:
#import "@preview/tablex:0.0.8": tablex, colspanx, rowspanx
#tablex(
columns: 3,
map-hlines: h => (..h, stroke: blue),
map-vlines: v => (..v, stroke: green + 2pt),
colspanx(2)[a], (), [b],
[c], rowspanx(2)[d], [ed],
[f], (), [g]
)
Cells can be customized entirely. Instead of specifying content (e.g. [text]
) as a table item, you can specify cellx(property: a, property: b, ...)[text]
, which allows you to customize properties, such as:
colspan: 2
(same as using colspanx(2, ...)[...]
)rowspan: 3
(same as using rowspanx(3, ...)[...]
)align: center
(override whole-table alignment for this cell)fill: blue
(fill just this cell with that color)inset: 0pt
(override inset/internal padding for this cell - note that this can look off unless you use auto columns and rows)x: 5
(arbitrarily place the cell at the given column, beginning at 0 - may error if conflicts occur)y: 6
(arbitrarily place the cell at the given row, beginning at 0 - may error if conflicts occur)Additionally, instead of specifying content to the cell, you can specify a function (column, row) => content
, allowing each cell to be aware of where it's positioned. (Note that positions are recorded in the cell's .x
and .y
attributes, and start as auto
unless you specify otherwise.)
For example:
#import "@preview/tablex:0.0.8": tablex, cellx, colspanx, rowspanx
#tablex(
columns: 3,
fill: red,
align: right,
colspanx(2)[a], (), [beeee],
[c], rowspanx(2)[d], cellx(fill: blue, align: left)[e],
[f], (), [g],
// place this cell at the first column, seventh row
cellx(colspan: 3, align: center, x: 0, y: 6)[hi I'm down here]
)
To customize multiple cells at once, you have a few options:
map-cells: cell => cell
(given a cell, returns a new cell). You can use this to customize the cell's attributes, but also to change its positions (however, avoid doing that as it can easily generate conflicts). You can access the cell's position with cell.x
and cell.y
. All other attributes are also accessible and changeable (see the Reference
further below for a list). Return something like (..cell, fill: blue)
, for example, to ensure the other properties (including the cell type marker) are kept. (Calling cellx
here is not necessary. If overriding the cell's content, use content: [whatever]
). This is useful if you want to, for example, customize a cell's fill color based on its contents, or add some content to every cell, or something similar.
map-rows: (row_index, cells) => cells
(given a row index and all cells in it, return a new array of cells). Allows customizing entire rows, but note that the cells in the cells
parameter can be none
if they're some position occupied by a colspan or rowspan of another cell. Ensure you return the cells in the order you were given, including the none
s, for best results. Also, you cannot move cells here to another row. You can change the cells' columns (by changing their x
property), but that will certainly generate conflicts if any col/rowspans are involved (in general, you cannot bulk-change col/rowspans without map-cells
).
map-cols: (col_index, cells) => cells
(given a column index and all cells in it, return a new array of cells). Similar to map-rows
, but for customizing columns. You cannot change the column of any cell here. (To do that, map-cells
is required.) You can, however, change its row (with y
, but do that sparingly), and, of course, all other properties.
Note: Execution order is map-cells
=> map-rows
=> map-cols
.
Example:
#import "@preview/tablex:0.0.8": tablex, colspanx, rowspanx
#tablex(
columns: 4,
auto-vlines: true,
// make all cells italicized
map-cells: cell => {
(..cell, content: emph(cell.content))
},
// add some arbitrary content to entire rows
map-rows: (row, cells) => cells.map(c =>
if c == none {
c // keeping 'none' is important
} else {
(..c, content: [#c.content\ *R#row*])
}
),
// color cells based on their columns
// (using 'fill: (column, row) => color' also works
// for this particular purpose)
map-cols: (col, cells) => cells.map(c =>
if c == none {
c
} else {
(..c, fill: if col < 2 { blue } else { yellow })
}
),
colspanx(2)[a], (), [b], [J],
[c], rowspanx(2)[dd], [e], [K],
[f], (), [g], [L],
)
Another example (summing columns):
#gridx(
columns: 3,
rows: 6,
fill: (col, row) => (blue, red, green).at(calc.rem(row + col - 1, 3)),
map-cols: (col, cells) => {
let last = cells.last()
last.content = [
#cells.slice(0, cells.len() - 1).fold(0, (acc, c) => if c != none { acc + eval(c.content.text) } else { acc })
]
last.fill = aqua
cells.last() = last
cells
},
[0], [5], [10],
[1], [6], [11],
[2], [7], [12],
[3], [8], [13],
[4], [9], [14],
[s], [s], [s]
)
Filled cells will partially overlap with horizontal lines above them (see https://github.com/PgBiel/typst-tablex/issues/4).
Table lines don't play very well with column and row gutter when a colspan or rowspan is used. They may be missing or be cut off by gutters.
Repeatable table headers might not behave properly depending on the size of your document or other factors (https://github.com/PgBiel/typst-tablex/issues/43).
Using tablex (especially when using repeatable header rows) may cause a warning, "layout did not converge within 5 attempts", to appear on recent Typst versions (https://github.com/PgBiel/typst-tablex/issues/38). This warning is due to how tablex works internally and is not your fault (in principle), so don't worry too much about it (unless you're sure it's not tablex that is causing this).
Rows with fractional height (such as 2fr
) have zero height if the table spans more than one page. This is because fractional row heights are calculated on the available height of the first page of the table, which is something that the default #table
can circumvent using internal code. This won't be fixed for now. (Columns with fractional width work fine, provided all pages the table is in have the same width, and the page width isn't auto
(which forces fractional columns to be 0pt, even in the default #table
).)
Rotation (via Typst's #rotate
) of text only affects the visual appearance of the text on the page, but does not change its dimensions as they factor into the layout.
This leads to certain visual issues, such as rotated text potentially overflowing the cell height without being hyphenated or, inversely, being hyphenated even though there is enough space vertically (https://github.com/PgBiel/typst-tablex/issues/59).
This is a known issue with Typst (perhaps, in the future, #rotate
may get a setting to affect layout).
As a workaround for the text hyphenation problem, the content can be boxed (and thus grouped together) with #box
(e.g., rowspanx(7, box(rotate(-90deg, [*donothyphenatethis*])))
), or hyphenation can be prevented by setting #text(hyphenate: false, ...)
(e.g., colspanx(2, text(hyphenate: false, rotate(-90deg, [*donothyphenatethis*])))
), as also discussed in https://github.com/PgBiel/typst-tablex/issues/59;
another alternative is to use #place
, e.g. aligning to center + horizon
: cellx(place(center + horizon, rotate(-90deg, [*donothyphenatethis*])))
, which probably allows the most control over the in-cell layout, since it simply draws the rotated content without having it occupy any space (letting you define that by yourself, e.g. using box(width: 1em, height: 2em, place(...))
).
rotatex
function which produces a rotated element with the appropriate sizes, such that tablex may recognize its size accordingly and avoid visual glitches.tablex
can potentially be slower and/or take longer to compile than the default table
(especially when the table spans a lot of pages). Please use the latest Typst version to reduce this problem (each version has been bringing further improvements in this sense). Still, we are looking for ways to better optimize the library (see more discussion at https://github.com/PgBiel/typst-tablex/issues/5 - feel free to give some input!). However, re-compilation is usually fine thanks to Typst's built-in memoization.
The internals of the library still aren't very well documented; I plan on adding more info about this eventually.
Please open a GitHub issue for anything weird you come across (make sure others haven't reported it first).
cellx
: Represents a table cell, and is initialized as follows:
#let cellx(content,
x: auto, y: auto,
rowspan: 1, colspan: 1,
fill: auto, align: auto,
inset: auto,
fit-spans: auto
) = (
tablex-dict-type: "cell",
content: content,
rowspan: rowspan,
colspan: colspan,
align: align,
fill: fill,
inset: inset,
fit-spans: fit-spans,
x: x,
y: y,
)
where:
tablex-dict-type
is the type markercontent
is the cell's content (either content
or a function with (col, row) => content
)rowspan
is how many rows this cell spans (default 1)colspan
is how many columns this cell spans (default 1)align
is this cell's align override, such as "center" (default auto
to follow the rest of the table)fill
is this cell's fill override, such as "blue" (default auto
to follow the rest of the table)inset
is this cell's inset override, such as 5pt
(default auto
to follow the rest of the table)fit-spans
allows overriding the table-wide fit-spans
setting for this specific cell (e.g. if this cell has a colspan
greater than 1, fit-spans: (x: true)
will cause it to not affect the sizes of auto
columns).x
is the cell's column index (0..len-1) - auto
indicates it wasn't assigned yety
is the cell's row index (0..len-1) - auto
indicates it wasn't assigned yethlinex
: represents a horizontal line:
#let hlinex(
start: 0, end: auto, y: auto,
stroke: auto,
stop-pre-gutter: auto, gutter-restrict: none,
stroke-expand: true,
expand: none
) = (
tablex-dict-type: "hline",
start: start,
end: end,
y: y,
stroke: stroke,
stop-pre-gutter: stop-pre-gutter,
gutter-restrict: gutter-restrict,
stroke-expand: stroke-expand,
expand: expand,
parent: none,
)
where:
tablex-dict-type
is the type markerstart
is the column index where the hline starts from (default 0
, a.k.a. the beginning)end
is the last column the hline touches (default auto
, a.k.a. all the way to the end)
colspan
larger than 1, even if their spans (start
-end
) include that cell.y
is the index of the row at the top of which the hline is drawn. (Defaults to auto
, a.k.a. depends on where you placed the hline
among the table items - it's always on the top of the row below the current one.)stroke
is the hline's stroke override (defaults to auto
, a.k.a. follow the rest of the table).stop-pre-gutter
: When true
, the hline will not be drawn over gutter (which is the default behavior of tables). Defaults to auto
which is essentially false
(draw over gutter).gutter-restrict
: Either top
, bottom
, or none
. Has no effect if row-gutter
is set to none
. Otherwise, defines if this hline
should be drawn only on the top of the row gutter (top
); on the bottom (bottom
); or on both the top and the bottom (none
, the default). Note that top
and bottom
are alignment values (not strings).stroke-expand
: When true
, the hline will be extended as necessary to cover the stroke of the vlines going through either end of the line. Defaults to true
.expand
: Optionally extend the hline by an arbitrary length. When none
, it is not expanded. When a length (such as 5pt
), it is expanded by that length on both ends. When an array of two lengths (such as (5pt, 10pt)
), it is expanded to the left by the first length (in this case, 5pt
) and to the right by the second (in this case, 10pt
). Defaults to none
.parent
: An internal attribute determined when splitting lines among cells. (It should always be none
on user-facing interfaces.)vlinex
: represents a vertical line:
#let vlinex(
start: 0, end: auto, x: auto,
stroke: auto,
stop-pre-gutter: auto, gutter-restrict: none,
stroke-expand: true,
expand: none
) = (
tablex-dict-type: "vline",
start: start,
end: end,
x: x,
stroke: stroke,
stop-pre-gutter: stop-pre-gutter,
gutter-restrict: gutter-restrict,
stroke-expand: stroke-expand,
expand: expand,
parent: none,
)
where:
tablex-dict-type
is the type markerstart
is the row index where the vline starts from (default 0
, a.k.a. the top)end
is the last row the vline touches (default auto
, a.k.a. all the way to the bottom)
rowspan
larger than 1, even if their spans (start
-end
) include that cell.x
is the index of the column to the left of which the vline is drawn. (Defaults to auto
, a.k.a. depends on where you placed the vline
among the table items.)
vline
to be placed after all columns, its x
value will be equal to the amount of columns (which isn't a valid column index, but it's what is used here).stroke
is the vline's stroke override (defaults to auto
, a.k.a. follow the rest of the table).stop-pre-gutter
: When true
, the vline will not be drawn over gutter (which is the default behavior of tables). Defaults to auto
which is essentially false
(draw over gutter).gutter-restrict
: Either left
, right
, or none
. Has no effect if column-gutter
is set to none
. Otherwise, defines if this vline
should be drawn only to the left of the column gutter (left
); to the right (right
); or on both the left and the right (none
, the default). Note that left
and right
are alignment values (not strings).stroke-expand
: When true
, the vline will be extended as necessary to cover the stroke of the hlines going through either end of the line. Defaults to true
.expand
: Optionally extend the vline by an arbitrary length. When none
, it is not expanded. When a length (such as 5pt
), it is expanded by that length on both ends. When an array of two lengths (such as (5pt, 10pt)
), it is expanded towards the top by the first length (in this case, 5pt
) and towards the bottom by the second (in this case, 10pt
). Defaults to none
.parent
: An internal attribute determined when splitting lines among cells. (It should always be none
on user-facing interfaces.)The occupied
type is an internal type used to represent cell positions occupied by cells with colspan
or rowspan
greater than 1.
Use is-tablex-cell
, is-tablex-hline
, is-tablex-vline
and is-tablex-occupied
to check if a particular object has the corresponding type marker.
colspanx
and rowspanx
are shorthands for setting the colspan
and rowspan
attributes of cellx
. They can also be nested (one given as an argument to the other) to combine their properties (e.g., colspanx(2)(rowspanx(3)[a])
). They accept all other cell properties with named arguments. For example, colspanx(2, align: center)[b]
is equivalent to cellx(colspan: 2, align: center)[b]
.
gridx
is equivalent to tablex
with auto-lines: false
; see below.
tablex:
The main function for creating a table with this library:
#let tablex(
columns: auto, rows: auto,
inset: 5pt,
align: auto,
fill: none,
stroke: auto,
column-gutter: auto, row-gutter: auto,
gutter: none,
repeat-header: false,
header-rows: 1,
header-hlines-have-priority: true,
auto-lines: true,
auto-hlines: auto,
auto-vlines: auto,
map-cells: none,
map-hlines: none,
map-vlines: none,
map-rows: none,
map-cols: none,
..items
) = {
// ...
}
Parameters:
columns
: The sizes (widths) of each column. They work just like regular table
's columns, and can be:
1pt
, 2em
, 100%
, ...), including fractional (2fr
), to specify the width of each column
columns: (2pt, 3em)
will give you two columns: one with a width of 2pt
and another with the width of 3em
(3 times the font size).
49%
, are considered fixed widths as they are always multiplied by the full page width (minus margins) for columns. Thus, a column with a size of 100%
would span your whole page (even if there are other columns).auto
may be specified to automatically resize the column based on the largest width of its contents, if possible - this is the most common column width choice, as it just delegates the column sizing job to tablex!
auto
-sized column contains two cells with Hello world!
and Bye!
as contents, tablex will try to make the column large enough for Hello world!
(the cell with largest potential width) to fit in a single line.auto
columns when they would otherwise cause the table to overrun the page's normal width (i.e. the width between the page's lateral margins).
2pt
, 3em
or 49%
) are not subject to this size reduction; thus, if you specify all columns' widths with fixed lengths, your table could become larger than the page's width! (In such a case, auto
columns would be reduced to a size of zero, as there would be no available space anymore!)1fr
, 2fr
...) for columns, the available space (remaining page width, after calculating all other columns' sizes) is divided between them, weighted on the fraction value of each column.
(1fr, 2fr)
, the available space will be divided by 3 (1 + 2), and the first column will have 1/3 of the space, while the second will have 2/3.
(1fr, 1fr)
would cause both columns to have equal length (1/2 and 1/2 of the available space).1fr
), it will occupy the entire available space.auto
width (the columns will have width zero) - this is because those pages theoretically have infinite width (they can expand indefinitely), so having columns spanning the entire available width is then impossible!columns: 2pt
is equivalent to columns: (2pt,)
, which translates to a single column of width 2pt
.4
), as a shorthand for (auto,) * 4
(that many auto
columns)
columns: 4
will give you four auto
columns).rows
: The sizes (heights) of each row. They follow the exact same format as columns
, except that the "available space" is infinite (auto rows can expand as much as is needed, as the table can add rows over multiple pages).
49%
) are fixed width lengths, like in columns
; however, here, they are multiplied by the page's full height (minus margins), and not width.auto
, the extra ones will also be auto
, for example.)
(# columns)
multipled by (# rows)
. In this case, you will have an extra row for each (# columns)
cells after the limit. In other words, the amount of columns is always fixed (determined by the amount of widths in the array given to columns
), but the amount of rows can vary depending on your input of cells to the table.y
coordinate can also cause your table to have extra rows (enough rows to reach the cell at that coordinate).#table
.inset
: Inset/internal padding to give to each cell. Can be either a length (same inset from the top, bottom, left and right of the cell), or a dictionary (e.g. (left: 5pt, right: 10pt, bottom: 2pt, top: 4pt)
, or even (left: 5pt, rest: 10pt)
to apply the same value to the remaining sides). Defaults to 5pt
(the #table
default).
align
: How to align text in the cells. Defaults to auto
, which inherits alignment from the outer context. Must be either auto
, an alignment
(such as left
or top
), a 2d alignment
(such as left + top
), an array
of alignment/2d alignment (one for each column in the table - if there are more columns than alignment values, they will alternate); or a function (column, row) => alignment/2d alignment
(to customize for each individual cell).
fill
: Color with which to fill cells' backgrounds. Defaults to none
, or no fill. Must be either a color
, such as blue
; an array
of colors (one for each column in the table - if there are more columns than colors, they will alternate); or a function (column, row) => color
(to customize for each individual cell).
stroke
: Indicates how to draw the table lines. Defaults to the current line styles in the document. For example: 5pt + red
to change the color and the thickness.
column-gutter
: optional separation (length) between columns (such as 5pt
). Defaults to none
(disable). At the moment, looks a bit ugly if your table has a hline
attempting to cross a colspan
.
row-gutter
: optional separation (length) between rows. Defaults to none
(disable). At the moment, looks a bit ugly if your table has a vline
attempting to cross a rowspan
.
gutter
: Sets a length to both column-
and row-gutter
at the same time (overridable by each).
repeat-header
: Controls header repetition. If set to true
, the first row (or the amount of rows specified in header-rows
), including its rowspans, is repeated across all pages this table spans. If set to false
(default), the aforementioned header row is not repeated in any page. If set to an integer (such as 4
), repeats for that many pages after the first, then stops. If set to an array of integers (such as (3, 4)
), repeats only on those pages relative to the table's first page (page 1 here is where the table is, so adding 1
to said array has no effect).
header-rows
: minimum amount of rows for the repeatable
header. 1 by default. Automatically increases if
one of the cells is a rowspan that would go beyond the
given amount of rows. For example, if 3 is given,
then at least the first 3 rows will repeat.
header-hlines-have-priority
: if true
, the horizontal
lines below the header being repeated take priority
over the rows they appear atop of on further pages.
If false
, they draw their own horizontal lines.
Defaults to true
.
true
. If this option is false
, the header will repeat, but the blue hline will not.rtl
: if true, the table is horizontally flipped. That is, cells and lines are placed in the opposite order (starting from the right), and horizontal lines are flipped.
This is meant to simulate the behavior of default Typst tables when set text(dir: rtl)
is used,
and is useful when writing in a language with a RTL (right-to-left) script.
Defaults to false
.
auto-lines
: Shorthand to apply a boolean to both auto-hlines
and auto-vlines
at the same time (overridable by each). Defaults to true
.
auto-hlines
: If true
, draw a horizontal line on every line where you did not manually draw one; if false
, no hlines other than the ones you specify (via hlinex
) are drawn. Defaults to auto
(follows auto-lines
, which in turn defaults to true
).
auto-vlines
: If true
, draw a vertical line on every line where you did not manually draw one; if false
, no vlines other than the ones you specify (via vlinex
) are drawn. Defaults to auto
(follows auto-lines
, which in turn defaults to true
).
map-cells
: A function which takes a single cellx
and returns another cellx
, or a content
which is converted to cellx
by cellx[#content]
. You can customize the cell in pretty much any way using this function; just take care to avoid conflicting with already-placed cells if you move it.
map-hlines
: A function which takes each horizontal line object (hlinex
) and returns another, optionally modifying its properties. You may also change its row position (y
). Note that this is also applied to lines generated by auto-hlines
.
map-vlines
: A function which takes each horizontal line object (vlinex
) and returns another, optionally modifying its properties. You may also change its column position (x
). Note that this is also applied to lines generated by auto-vlines
.
map-rows
: A function mapping each row of cells to new values or modified properties.
Takes (row_num, cell_array)
and returns
the modified cell_array
. Note that, with your function, they
cannot be sent to another row. Also, please preserve the order of the cells. This is especially important given that cells may be none
if they're actually a position taken by another cell with colspan/rowspan. Make sure the none
values are in the same indexes when the array is returned.
map-cols
: A function mapping each column of cells to new values or modified properties.
Takes (col_num, cell_array)
and returns
the modified cell_array
. Note that, with your function, they
cannot be sent to another column. Also, please preserve the order of the cells. This is especially important given that cells may be none
if they're actually a position taken by another cell with colspan/rowspan. Make sure the none
values are in the same indexes when the array is returned.
fit-spans
: either a dictionary (x: bool, y: bool)
or just bool
(e.g. just true
is converted to (x: true, y: true)
). When given (x: true)
, colspans won't affect the sizes of auto
columns. When given (y: true)
, rowspans won't affect the sizes of auto
rows. By default, this is equal to (x: false, y: false)
(equivalent to just false
), which means that colspans will cause the last spanned auto
column to expand (depending on the contents of the cell) and rowspans will cause the last spanned auto
row to expand similarly.
(x: true)
to prevent unexpected expansion of auto
columns after using a colspan, which can happen when a colspan spans both a fractional-size column (e.g. 1fr
) and an auto
-sized column. Can be applied to rows too through (y: true)
or (x: true, y: true)
, if needed, however.fit-spans
option to tablex
and cellx
(https://github.com/PgBiel/typst-tablex/pull/111)
(x: bool, y: bool)
. When set to (x: true)
, colspans won't affect the sizes of auto
columns. When set to (y: true)
, rowspans won't affect the sizes of auto
rows.false
, equivalent to (x: false, y: false)
, that is, colspans and rowspans affect the sizes of auto
tracks (columns and rows) by default (expanding the last spanned track if the colspan/rowspan is too large).auto
-sized track to inadvertently expand.auto
column sizing received multiple improvements and bug fixes. Tables should now have more natural column widths. (https://github.com/PgBiel/typst-tablex/pull/109, https://github.com/PgBiel/typst-tablex/pull/116)
auto
columns being needlessly expanded in some cases (https://github.com/PgBiel/typst-tablex/issues/56, https://github.com/PgBiel/typst-tablex/issues/78)fit-spans
option as needed, or use fixed-size columns instead.tablex
tables might now become up to 20% faster to cold compile. Give it a shot!auto
height (https://github.com/PgBiel/typst-tablex/pull/108).I have begun work on bringing many tablex improvements to built-in Typst tables! In that regard, you can now sponsor my work on tablex and improving Typst tables via GitHub Sponsors! Consider taking a look :)
line
in tablex cells would misbehave (https://github.com/PgBiel/typst-tablex/issues/80)
typst.toml
for better discoverability (https://github.com/PgBiel/typst-tablex/issues/91)rtl: true
(https://github.com/PgBiel/typst-tablex/issues/58).
set text(dir: rtl)
, however we can't detect that setting from tablex at this moment (it isn't currently possible to fetch set rules in Typst).#tablex(rtl: true, ...)
to flip your table horizontally if you're writing a document in RTL (right-to-left) script. (You can use e.g. #let old-tablex = tablex
followed by #let tablex(..args) = old-tablex(rtl: true, ..args)
to not have to repeat the rtl
parameter every time.)box
's dictionary inset syntax on tablex (https://github.com/PgBiel/typst-tablex/issues/54).
#tablex(inset: (left: 5pt, top: 10pt, rest: 2pt), ...)
.#rotate
problems to "Known Issues" in the README (https://github.com/PgBiel/typst-tablex/pull/60).columns
and rows
(https://github.com/PgBiel/typst-tablex/issues/53).layout()
function.
auto
width (https://github.com/PgBiel/typst-tablex/issues/44; https://github.com/PgBiel/typst-tablex/issues/39)auto
-sized pages (https://github.com/PgBiel/typst-tablex/issues/47).em
strokes and other types of strokes (https://github.com/PgBiel/typst-tablex/issues/49).typst.toml
to support Typst v0.6.0's soon-to-be-released package manager (see https://github.com/PgBiel/typst-tablex/issues/22).hlinex(gutter-restrict: top)
causing the hline to just disappear (https://github.com/PgBiel/typst-tablex/issues/20).gutter-restrict
lines disappearing when there's no gutter (https://github.com/PgBiel/typst-tablex/issues/21).fill:
and align:
now accept arrays of values for each column (https://github.com/PgBiel/typst-tablex/issues/13).
fill: (red, blue)
would fill the first column with red, the second column with blue, and any further columns would alternate between the two fill colors.auto
rows and columns when a rowspan or colspan was used (https://github.com/PgBiel/typst-tablex/issues/11).auto
column when it was too long (https://github.com/PgBiel/typst-tablex/issues/6).stroke-expand: false
on your lines.expand: (length, length)
; e.g. expand: (5pt, 10pt)
will increase your horizontal line 5pt to the left and 10pt to the right (or, for a vertical line, 5pt to the top and 10pt to the bottom).
Initial release.
tablex
, cellx
, hlinex
, vlinex
gridx
, rowspanx
, colspanx
#table
parity
columns:
, rows:
auto
columns and rowsauto
auto
columns based on available spacefill
color
for general fill)(column, row) => color
)align
alignment
and 2d alignment
apply to all cells)(column, row) => alignment/2d alignment
)inset
gutter
column-gutter
row-gutter
stop-pre-gutter
: Makes the hline/vline not transpose gutter boundariesgutter-restrict
: Makes the hline/vline not draw on both sides of a gutter boundary, and instead pick one (top/bottom; left/right)stroke
none
for no stroke#tablex
cellx
, hlinex
, vlinex
)hlinex
, vlinex
stroke
colspanx
, rowspanx
hlinex
and vlinex
with end: auto
cellx(colspanx:, rowspanx:)
colspanx(rowspanx())
)colspanx(2, align: left)[a]
)repeat-header
: Control header repetition
true
: Repeat on all pagesfalse
(default): Do not repeatheader-rows
: Indicate what to consider as a "header"
none
or 0
: no header (disables header repetition regardless of repeat-header
)cellx
cellx(x:, y:)
align
overridefill
overrideinset
override
auto
cols/rowsmap-cells
on a single cell)auto-hlines
- true
to place on all lines without hlines, false
otherwiseauto-vlines
- similarauto-lines
- controls both simultaneously (defaults to true
)map-cells
- Customize every single cellmap-hlines
- Customize each horizontal linemap-vlines
- Customize each vertical linemap-rows
- Customize entire rows of cellsmap-cols
- Customize entire columns of cellsMIT license (see the LICENSE
file).