i have a long setup for header similar to
#set page(
numbering: "1",
header: context [
HEADER
#h(1fr)
#counter(page).display()
],
)
= First Chapter
#lorem(200)
which i would move to a function (to better structure the code). I assume i would have to write something like:
#let set-header: it => context [
HEADER
#h(1fr)
#counter(page).display()
],
#set page(
numbering: "1",
header: set-header()
)
= First Chapter
#lorem(200)
What is the correct approach?
1 Like
sijo
May 22, 2026, 8:45am
2
Hi, welcome to the forum!
You could define the function with the => syntax (anonymous function) but this is a function with zero argument so you would write: #let set-header = () => context [...]. But it’s a bit weird to define an anonymous function just to store it in a variable set-header. You would rather define a set-header function directly with
#let set-header() = context [ ... ]
But the body of the function is constant so you don’t need a function at all! So the right way to do this here is to define the header content simply as a variable:
#let header = context [ ... ]
and use it as #set page(header: header, ...).
1 Like
@sijo is 100% right that a function is unnecessary here.
Creating a function can have benefits though, such as the ability to easily change the header text when you want.
#let set-header(header-text) = context [
#header-text
#h(1fr)
#counter(page).display()
]
#set page(numbering: "1")
#set page(header: set-header[Header Text 1])
#set page(header: set-header[Header Text 2])
Note, however, that a new page is created every time set page() is called. So you would probably only use this at the start of a new chapter/section.
Something that is not obvious but can have surprising effects on layout is the use of [] here. This even has its own GitHub issue:
opened 10:05PM - 31 Aug 25 UTC
docs
### Description
This is one of the most reoccurring things that I absolutely ha… te for many reasons. I don't think there is a dedicated issue about this, but I mentioned it many times already, but it's all mostly lost as I don't keep track of everything. I created this to be able to link to _somewhere_, until the documentation has the requested section.
As seen in https://github.com/typst/typst/issues/6273, or https://forum.typst.app/t/how-does-one-place-content-relative-to-the-page-margin/4373, or https://forum.typst.app/t/how-to-properly-justify-text-in-a-figure-caption/5822/2, using square brackets where you (only) use code inside (instead of using curly braces), or for things that should be an inline no-leading-trailing whitespaces content — these are one of the most common use cases where square brackets are used carelessly. More time than not such carelessness _will_ affect your, or users' of your package/template document! It happened to me multiple times, and I also was using
```typ
#func()[
...
]
```
_a lot_ in the early days (partially because this way things were better formatted with typstfmt, rather than using curly braces). Now I learned my lessons and become very sensitive to this, so I rarely use it, only where necessary or knowing the consequences of it (also with typstyle now formatting everything pretty nicely, and having text wrapping, bad source formatting is no longer an issue).
So, the actually dangerous part is using square brackets in "multi-line mode":
```typ
#func[
block-like syntax is multiline
]
#func[
this is also multiline]
#func[this as well
// That's right.
]
```
And the _actually-actually_ dangerous part is the spaces right after `[` and right before `]`. In case of caption, or other space-sensitive context, using multi-line square brackets will inject these invisible spaces into your document, potentially altering its layout. A non-multi-line example, but still relevant, is https://github.com/pammacdotnet/IJIMAI/pull/3/commits/4b0adabfd27cd33f54bc7b5b8e3ee19dd1e71236 (https://github.com/pammacdotnet/IJIMAI/pull/3), which fixes a double whitespace in table caption. Here, a more clearly visible whitespace is inserted. Some most definitely would think that it doesn't matter, yet it does! At least if you care about the typographical quality of your documents. (I do!)
Newlines also create `space`s (`[ ].func()`), as well as their combination with comments (multiple whitespaces). This is why, for example, dumping a bunch of (or even a single) set/show-set/etc. rules in something that is supposed to be included inline into some other document structure (that may or may not handle the trailing/leading whitespaces) is a bad idea. It's totally normal if you write one or multiply paragraphs right after that (scoped rules):
```typ
#[
#set text(10pt)
#set table(stroke: 2pt + red)
Some paragraph #lorem(50).
You don't even have to indent anything. It will look the same if you remove indentation from the source file.
]
```
Another common use case is using labeled metadata: `metadata(...)<label>`. Since labels can only be attached in markup mode, you have to write this in markup mode, like figure or heading, or specifically wrap this in `[]`: `[#metadata(...)<label>]`. And with this common use case, there is a common mistake of having whitespace between the two. At least a mistake when this can be embedded/inserted inline somewhere else (like `metadata`, but not `heading`/`figure`). This was an issue in https://github.com/typst-community/prequery/pull/3.
This is why a carefully crafted section on this matter is necessary in the official documentation. Since [the tutorials will be completely rewritten](https://github.com/typst/typst/pull/6417#issuecomment-3103908588) anyway, this might be a great place to integrate such a warning, when users are still in the learning phase. Teaching _the right way_ early on is crucial. I think a better knowledge of internals — which exact things 100% will be affected by this issue and which won't — will be very helpful in providing a more practical way of educating people about the square bracket issue.
I can update and extend the examples further (after all, I see this rather frequently on the forum or in a random package I might use and debug), but I think this should be enough.
P.S. There is a similar issue with `regex`/`str` text show rules that needs to be addressed in the docs (about the performance and overall hackiness of it): https://github.com/typst/typst/issues/6525, https://github.com/typst/typst/issues/5209, https://forum.typst.app/t/how-to-properly-justify-text-in-a-figure-caption/5822/4?u=andrew.
A simple change is to use {} instead:
#let set-header(header-text) = context {
header-text
h(1fr)
counter(page).display()
}
3 Likes
I think u want to get rid of the non-content stuff like h(1fr), counter(), SURE u can do it this way
#let Page = (
num: "1",
hdr: [HEADER],)
#set page(
numbering: Page.num,
header: context {
Page.hdr
h(1fr)
counter(page).display()},)
= First Chapter
#lorem(200)