How can I make sure the figure and its caption will never be put to different pages?

Hello,
I want to make sure that the caption will always stick to its figure without having a pagebreak between them. Whenever the figure is a table, the table should be allowed to split over pages, but the caption should never.

1 Like

As stated in Figure Function – Typst Documentation and Why won't this table in a figure break across pages? - #3 by PgBiel, you just need to make figure breakable with the show-set rule:

#show figure.where(kind: table): set block(breakable: true, sticky: true)
#v(21pt)
#v(1pt)
#figure(table(columns: 3, ..range(33).map(_ => lorem(15))), caption: "caption")

Additionally, since this makes caption split from table, probably because there is no default rule to prevent this, enabling block.sticky fixes that. Though technically, I think this should be better:

#show figure.where(kind: table): set block(breakable: true)
#show table: set block(sticky: true)

Because otherwise block after figure will stick to the figure, I think. But now any bare table also will be sticky, so a more accurate solution would be

#show figure.where(kind: table): set block(breakable: true)
#show figure.where(kind: table): it => {
  show table: set block(sticky: true)
  it
}

Though you won’t undo this nested show-set rule after you apply it. At some point you would be able to do show-show-set rule or something like that.

By default, since the figure is not breakable, the caption will never be put on a separate page, as caption and figure body are both in unbreakable block.

@Andrew’s solution probably works 99% of the time, but for some reason this figure will still have its caption on the next page:

#figure(
  table(
    columns: 1,
    ..range(40).map(_ => "test")
  ),
  caption: "test"
)

Mentioned in Prevent page break directly between figure body and caption for breakable figures · Issue #5357 · typst/typst · GitHub.

This happens with raw blocks too:

// #show figure.where(kind: table): set block(breakable: true)
// #show figure.where(kind: table): it => {
//   show table: set block(sticky: true)
//   it
// }
// #figure(table(..range(40).map(_ => "test")), caption: "test")
#align(center, block(/* breakable: false, */ {
  set block(spacing: 0pt)
  block(sticky: true, range(40).map(_ => block(stroke: 1pt, inset: 5pt)[test]).join())
  v(0.65em)
  block[Table 1: test]
}))