Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Counters And Resets

Each numbered original mark advances one counter, selected by its counter owner and series. The first numbered original in an unused stream gets 1. Body attachments, standalone bodies, references, repeated marks, pins, and explicit connections do not advance it. Note identity is independent of its displayed number, so resets do not create duplicate identities or change earlier notes.

An original whose resolved numbering is none takes no counter value and has count: none in queries. This includes local options and shared defaults, including note-family settings such as numbering: (margin-note: none). It shows no automatic marker; an explicit fixed marker: still displays. With non-none numbering, fixed markers and marker: none still advance the counter. References to a markerless note display ? by default; outlines keep the note with an empty badge.

Most note wrappers use series "default"; endnote wrappers use "endnote". Both counter functions default to "default". Pass series: "endnote" when reading or resetting that stream. Series names are literal strings, including punctuation and empty strings, with no reserved edge/component words.

Read, Set, Or Update

After #show: deixis-setup-notes:

Source

Current: #context deixis-note-counter(series: "notes")
#deixis-update-note-counter(10, series: "notes")
After setting: #context deixis-note-counter(series: "notes")
#deixis-update-note-counter(n => n + 2, series: "notes")
After updating: #context deixis-note-counter(series: "notes")
#deixis-update-note-counter(0, series: "notes")
After resetting: #context deixis-note-counter(series: "notes")

Complete source and preview setup

Output · Click to zoom

An unused stream reads as zero. The read returns an integer inside a native context; it is the current numbering value, not a total of all notes ever declared. Setting 10 makes the next numbered original 11. Resetting to 0 makes it 1. Other series remain unchanged. A callback receives the current value and returns the replacement integer; keep it a pure calculation of that value.

Direct values and callback results must be integers. Floats, booleans, strings, none, and other results are rejected without coercion. Convert a value deliberately before passing it if appropriate. Signed integers remain supported: setting -1 permits a next value of 0, while negative displayed counts require a numbering callback that handles them. The standard numbering patterns require nonnegative counts.

See the small reset example, where a native heading rule restarts one series for each section while another continues. There is no separate reset function: updating to 0 supplies it. For independent local collections, see scoped notes.

Counter Ownership

target: 0 selects the current scope. Negative integers select ancestors; going beyond the available stack falls back to the document. target: "page" always selects the document owner. A named string/label can precede its block, including forward synchronization chains. See scoped notes. Named targets select public block IDs; private automatic IDs and synthesized thread names are not target syntax. Select a thread through a named member. Strings and labels normalize to the same public name. See scopes.

A normal deixis-block has its own counter owner. Matching thread: values share an owner by default. sync-counters-with: explicitly shares numbering while retaining each block’s rendering ownership. Reading, setting, or resetting through any synchronized alias affects the same stream. Other owners remain independent. Scope and series are separate keys: (scope: "alpha", series: "beta:gamma") and (scope: "alpha:beta", series: "gamma") cannot alias each other. Automatic blocks, named blocks, and threads also use separate private owner namespaces. A block named "1" cannot share an automatic block’s numbering. Updates require a declared named target; passive reads of an unknown named target return zero.

The original mark owns its series and assigned number. An attached body inherits that series; omit series: on attached bodies and wrappers. An explicit series, even the same value, is rejected. A body target: override changes its rendering scope without moving the counter operation or changing the inherited series. A named standalone body’s series can organize a collection without creating a counter. Use explicit series on original marks or creating wrappers to choose their stream. Page-note queries retain native body order after resets, and outlines retain original source order. Neither sorts entries by their displayed count; see selection and queries.

Ordering And Placement

Place a reset in the main flow before the originals it should affect, usually at a section boundary. A later reset does not renumber an earlier original, body, reference, or reuse. Printing an endnote later keeps its original number. "page" is a document scope, and does not imply a reset on each physical page. For natural pagination, use the opt-in display policy below. Native rules and explicit boundaries remain useful for resetting the assigned counter itself.

Restart Footnote Markers On Each Page

Keep the simple wrapper and opt in through numbering:

Source

#let note = deixis-footnote.with(
  numbering: (restart: "page", pattern: "1"),
)
Text#note[An explanation.]

Complete source and preview setup

Output · Click to zoom

pattern defaults to "1"; "*" gives native symbol numbering, and a pure n => ... callback receives the page-local ordinal. For all subsequent originals, use #deixis-set(numbering: (restart: "page", pattern: "1")). Ordinary numbering strings/functions retain the existing behavior. Set numbering: "1" to return to the assigned counter. Explicit marker: content takes precedence.

See the editable page-numbering example.

Native Typst also supports a per-page recipe: reset counter(footnote) in the page header, as described in the native footnote documentation. That resets the native counter itself. This Deixis policy instead changes only the displayed ordinal; assigned counts and counter updates remain independent. Native footnotes still allocate page-footer bodies, while Deixis coordinates the original marker across separate bodies, reuse, references and outlines. Native labelled footnotes already support reuse and links too; the distinction is the assigned/displayed split and its integration across Deixis adapters.

Despite this section’s footnote example, the policy applies to every original Deixis mark and note adapter. Margin, inset and inline notes can use it too. Endnotes retain their original page’s marker when collected later, so repeated numbers can make that list ambiguous; choose independent series or ordinary numbering when readers need a unique label across the document. A standalone body has no original mark to rank; attaching a body with of: uses its original.

Each physical page starts at 1, independently for each counter owner and series. Ordering follows original declarations within that stream. All originals in the stream with non-none numbering consume a slot, including ones using a different pattern or custom marker; use a separate series when numbering sequences should be independent. Hidden numbered originals retain a slot, while unnumbered and omitted originals take none. Synchronized blocks share a stream; independent local blocks retain their owners.

A deixis-block that breaks across pages still collects its bodies into one list at its end, so page-local markers repeat inside that list (1, 2, 1, 2), as they do for endnotes. For multi-page blocks, either send the notes to each page’s footer with deixis-footnote.with(target: "page"), which then restart cleanly per page, or keep ordinary numbering inside the block. Threaded blocks share one stream: numbering continues across them on a page and restarts on the next.

The assigned counter is unchanged. note.count and deixis-note-counter report the source-order assigned value and current counter respectively, including explicit updates; unnumbered originals retain note.count: none. note.marker is the displayed content; references, outlines, attached bodies and reused marks share it. A reuse on another page keeps its original’s marker. Manual counter updates change assigned counts but do not change the page-local rank. This separation preserves identity and existing counter arithmetic. Multi-note references keep caller order and individual links; page-local markers are not collapsed into numeric ranges across pages.

Custom body renderers should use deixis-generate-body-marker (or the supplied marker-str content) to preserve that label. Their data.count remains the assigned counter, just like a query record’s count.

The page is the original mark’s physical page, using its painted return point where available; a spanning region uses its first painted page. Resetting the printed page counter does not merge physical pages. A separate or split footnote body retains the original marker even if its first fragment is on another page. This policy does not restart at a body fragment or move notes between pages.

Page-dependent marker widths can affect pagination. This mode requires extra layout passes; deeply nested contextual notes or pathological boundary changes can still exceed Typst’s convergence limit. Compiler warnings must be treated as failures. Test long documents with the intended fonts, symbol pattern and page size. Ordinary assigned numbering remains the default. Touying restarts per physical frame; its page-preamble recipe remains available for explicit host-controlled counter resets.

Restart At A Heading

Apply a native show rule after setup, selecting the heading level and series:

Source

#show heading.where(level: 1): it => {
  deixis-update-note-counter(0, target: "page", series: "notes")
  it
}
#let note = deixis-endnote.with(series: "notes")

= First Chapter
Text#note[Starts at one.]
= Second Chapter
Text#note[Starts at one again.]

#deixis-print-endnotes(series: "notes")

Complete source and preview setup

Output · Click to zoom

The reset runs before the heading’s title, so a new mark in that title gets 1 and the next original gets 2. Subheadings and other series are unaffected. Reused marks and references keep their original number, including when they precede that original. Print endnotes where wanted; printing does not restart their stream.

target: "page" deliberately selects document numbering even inside a block. Use target: 0 for the current scope; synchronized/threaded aliases share that reset. A native rule applies to every matching heading in its scope, including headings inside notes or custom output. If only selected headings should restart numbering, put the update and heading together in a small wrapper instead.

Slides And Explicit Page Boundaries

For Touying, reset in the theme’s page preamble, which runs before each rendered frame’s body. The presentation integration shows the full setup, host-owned page overlays, and a per-slide setting alternative. Select each stream explicitly:

Fragment · Requires surrounding code

#show: deixis-setup-notes.with(page-overlays: false)
#show: simple-theme.with(
  config-common(page-preamble: deixis-update-note-counter(
    0, target: "page", series: "default")),
  // Page layers as in the rendering guide.
)
#slide[...]

Each rendered animation frame starts at one, so persistent notes keep the same numbers across a pause; the next slide starts at one too. This is frame-local numbering, not one continuous stream across distinct slides. Use wrapper-created IDs across animation frames, as described in the rendering guide.

For a page boundary you create yourself, put #pagebreak() followed by #deixis-update-note-counter(0, target: "page", series: "notes") before its originals. An ordinary page break or an update of native counter(page) alone does not reset note numbering. This recipe does not cover pages formed by natural pagination. Touying emits its preamble with each frame’s header, before the frame body; other page headers and overlays run in native rendering order and are not a general substitute for a reset at an original’s boundary.

Resets Inside Live Bodies

Updates inside live body content retain native evaluation order. They are barriers to nested-original preparation: a mark is not extracted past a reset. Headings also retain live ordering, so a heading rule that generates an update runs before nested title/following marks. This conservatively applies to bodies containing headings even when those headings have no reset rule. Other nested originals can still register early at their body’s declaration.

Deferred/overlay bodies still evaluate in their actual rendering context, so a reset inside them is not a substitute for a main-flow section reset. In particular, background bodies run before that page’s main flow; foreground bodies run after it. Within an overlay, paint priority can also change the order of live counter updates. Use an explicit target when a shared owner must be selected deliberately.

Arbitrary custom show callbacks cannot be inspected before rendering. When such a callback changes note counters, put that state-dependent content in a native context to keep its declarations and generated updates in live order. In this excerpt, custom-content is the callback’s content:

Fragment · Requires surrounding code

#deixis-inline-note-body(inline: false)[#context { custom-content }]

Context-generated nested notes remain subject to Typst’s finite layout passes; this does not make arbitrarily deep registration chains converge.