How to reset the page counter after every section?

I am currently writing a template where a document has multiple sections; each section has the same table with different data, but the header and footer remain the same. I need to be able to start the page number from 1 for every new section.

I tried updating the page counter to 1 using pageCounter.update(1), but I was seeing unexpected behavior using this. Specifically, when a table runs over onto the second page, the numbering for the first page becomes 1/1, and for the second one, its 2/1, as compared to what I want being 1/2 and 2/2, respectively.

Any solutions for achieving this?

Hello @Div and welcome!
It would help if you provide a working example in addition to your detailed explanation. I have reproduced something that may be what you want.

Bear in mind that the total page number cannot be contextual per section. The final result will look like:

  • 1/N
  • 1/N, 2/N
  • 1/N, 2/N, 3/N
  • etc.

This is tracked at Support per chapter page numbering with total · Issue #4367 · typst/typst · GitHub.

There are other posts you might want to read on counters:

Finally, I cannot recommend more to read the awesome post on How to post in the Questions category :stuck_out_tongue:!

#show heading.where(level: 1): it => {
  pagebreak(weak: true)
  counter(page).update(1)
  it
}
#set page(numbering: "1 / 1")

= Section 1

#lorem(500)

= Section 2

#lorem(1000)

= Section 3

#lorem(2000)

Hey @Div, welcome to the forum! I’ve changed your question post’s title to better fit our guidelines: How to post in the Questions category

For future posts, make sure your title is a question you’d ask to a friend about Typst. Also, don’t forget to add some tags next time! :wink:

I just realized I terribly worded my question. Let me describe my problem again: I have tables that I am rendering inside a for loop. for each table, I need the page counter to start from 1, for which i am using
pageCounter.update(1)
at the end of the table. But what I am noticing is that for multipage tables, the current counter of the page is incrementing correctly, and the total page count remains 1 regardless.

code:
#for loop
let pageCounter = counter(page)
functionForTable
pageCounter.update(1)

i hope this gives more context haha

ah yes, apologies. thankyou will keep this in mind

1 Like

If you put the update after the table, then the compiler updates the page number after the pagebreak. You can put the update right before the table.

@quachpas - I tried the suggestion but does not do the trick.

The second time it starts with 5/5 and then subsequently it resets and then on 2nd page I get 1/5.

Please see this sample project that I have created -
https://typst.app/project/rPcPdAvoThkuHGGd7amNPR

#set page(
  columns: 1,
  paper: "a4",
  flipped: false,
  margin: (x: 16mm, y: 27.5mm),
  header: context {
    set text(14pt)
    counter(page).display(
    "1/1",
    both: true,
    )
  }
)

= How to reset the page counter?

#set text(14pt)

#let moore = csv("moore.csv")

#let long_table = table(
  columns: 2,
  ..for (.., year, count) in moore {
    (year, count)
  }
)

#let n = 2
#while n > 0 {
  context { counter(page).update(0) }
  [This is Moore's Law]
  long_table
  pagebreak()
  n = n - 1
}

We need the pagebreak() but that seems to interfere with the desired outcome… ?

The behaviour is tricky here. If you use footer instead of header, you will get a different result. Indeed, you are manually writing a contextual expression in the header/footer. So depending on its location in the document, it will return a different result. In addition to updating the page counter manually, that sets off too many edge cases.

I suggest to use the named arguments numbering and number-align instead, which are created for that specifically.

#set page(
  columns: 1,
  paper: "a4",
  flipped: false,
  margin: (x: 16mm, y: 27.5mm),
  numbering: "1/1",
  number-align: center + bottom, // or center + top
)

Also, you should use update(1) and not 0, as well as a weak pagebreak, which avoids blank pages.

The counter is resolved based on the logical order of things: The header is logically before the page content, which is followed by the footer. Thus, if you have a counter update within a page, it will be reflected in the footer, but not in the header.

However, there is an escape hatch: Typically, what you truly want is to reset the page counter between to pages. To do that, you can sandwich an update between two pagebreaks (with at least of them being weak), and Typst will correctly understand that it’s supposed to happen in the empty void between. (Unfortunately, this behaviour is not properly documented.)

The following example demonstrates the feature:

#set page(
  paper: "a8",
  margin: 40pt,
  header: context counter(page).display(),
  footer: context counter(page).display(),
)

A

// Try uncommenting each of the pagebreaks in turn:
// #pagebreak()
#counter(page).update(5)
// #pagebreak(weak: true)

B

With this, you can fix the first problem of your page numbering (that numbers are off by one). The second part (which is not exhibited by your example) is more problematic: The final value will always corresponds to the page number at the very end of the document, not to the end of the “section”. There is no built-in feature for this at the moment, but it can be realized with a slightly more manual setup, which I’ve implemented below:

// Returns the page total of the current section.
#let reset = <__reset>
#let subtotal() = {
  let loc = here()
  let list = query(selector(reset).after(loc))
  if list.len() > 0 { 
    counter(page).at(list.first().location()).first() - 1
  } else {
    counter(page).final().first()
  }
}

// A section of content with reset page numbering.
//
// Leaves a marker we can query to find the subtotal page number.
// Resets the page counter in between the pages.
#let section(it) = {
  pagebreak(weak: true)
  [#metadata(none)#reset]
  counter(page).update(1)
  pagebreak(weak: true)
  it
}

#let page-numbers = context numbering(
  "1 / 1",
  ..counter(page).get(),
  subtotal(),
)

#set page(
  paper: "a8",
  margin: 40pt,
  // Show both in the header & footer to see the effect.
  header: align(center, page-numbers),
  footer: align(center, page-numbers),
)

#section([= A] + lorem(30))
#section([= B] + lorem(60))
#section([= C] + lorem(30))
2 Likes

@laurmaedje - First of all, a big thank you for the details and the solution provided.

I did a quick test and it looks like header is good but footer still has a small issue. In the last page of every section it shows the counter of the last of page of the following section. So example - it shows 3 / 5 in the last page of the 1st section, 5 / 3 in the last page of the 2nd section. The last section shows 3 / 3 correctly.

May be we need to adjust the subtotal() function a bit?

Summary

You’re right, I missed this. I edited my post above with a bug fix. (Thanks to Sekoia on Discord for coming up with the fix!)

@laurmaedje - Thank you for the update. This works now :100: