Spaces:
Sleeping
Sleeping
description: | | |
An in-depth guide to setting page dimensions, margins, and page numbers in | |
Typst. Learn how to create appealing and clear layouts and get there quickly. | |
# Page setup guide | |
Your page setup is a big part of the first impression your document gives. Line | |
lengths, margins, and columns influence | |
[appearance](https://practicaltypography.com/page-margins.html) and | |
[legibility](https://designregression.com/article/line-length-revisited-following-the-research) | |
while the right headers and footers will help your reader easily navigate your | |
document. This guide will help you to customize pages, margins, headers, | |
footers, and page numbers so that they are the right fit for your content and | |
you can get started with writing. | |
In Typst, each page has a width, a height, and margins on all four sides. The | |
top and bottom margins may contain a header and footer. The set rule of the | |
[`{page}`]($page) element is where you control all of the page setup. If you | |
make changes with this set rule, Typst will ensure that there is a new and | |
conforming empty page afterward, so it may insert a page break. Therefore, it is | |
best to specify your [`{page}`]($page) set rule at the start of your document or | |
in your template. | |
```example | |
#set rect( | |
width: 100%, | |
height: 100%, | |
inset: 4pt, | |
) | |
>>> #set text(6pt) | |
>>> #set page(margin: auto) | |
#set page( | |
paper: "iso-b7", | |
header: rect(fill: aqua)[Header], | |
footer: rect(fill: aqua)[Footer], | |
number-align: center, | |
) | |
#rect(fill: aqua.lighten(40%)) | |
``` | |
This example visualizes the dimensions for page content, headers, and footers. | |
The page content is the page size (ISO B7) minus each side's default margin. In | |
the top and the bottom margin, there are stroked rectangles visualizing the | |
header and footer. They do not touch the main content, instead, they are offset | |
by 30% of the respective margin. You can control this offset by specifying the | |
[`header-ascent`]($page.header-ascent) and | |
[`footer-descent`]($page.footer-descent) arguments. | |
Below, the guide will go more into detail on how to accomplish common page setup | |
requirements with examples. | |
## Customize page size and margins { #customize-margins } | |
Typst's default page size is A4 paper. Depending on your region and your use | |
case, you will want to change this. You can do this by using the | |
[`{page}`]($page) set rule and passing it a string argument to use a common page | |
size. Options include the complete ISO 216 series (e.g. `"a4"` and `"iso-c2"`), | |
customary US formats like `"us-legal"` or `"us-letter"`, and more. Check out the | |
reference for the [page's paper argument]($page.paper) to learn about all | |
available options. | |
```example | |
>>> #set page(margin: auto) | |
#set page("us-letter") | |
This page likes freedom. | |
``` | |
If you need to customize your page size to some dimensions, you can specify the | |
named arguments [`width`]($page.width) and [`height`]($page.height) instead. | |
```example | |
>>> #set page(margin: auto) | |
#set page(width: 12cm, height: 12cm) | |
This page is a square. | |
``` | |
### Change the page's margins { #change-margins } | |
Margins are a vital ingredient for good typography: | |
[Typographers consider lines that fit between 45 and 75 characters best length | |
for legibility](http://webtypography.net/2.1.2) and your margins and | |
[columns](#columns) help define line widths. By default, Typst will create | |
margins proportional to the page size of your document. To set custom margins, | |
you will use the [`margin`]($page.margin) argument in the [`{page}`]($page) set | |
rule. | |
The `margin` argument will accept a length if you want to set all margins to the | |
same width. However, you often want to set different margins on each side. To do | |
this, you can pass a dictionary: | |
```example | |
#set page(margin: ( | |
top: 3cm, | |
bottom: 2cm, | |
x: 1.5cm, | |
)) | |
#lorem(100) | |
``` | |
The page margin dictionary can have keys for each side (`top`, `bottom`, `left`, | |
`right`), but you can also control left and right together by setting the `x` | |
key of the margin dictionary, like in the example. Likewise, the top and bottom | |
margins can be adjusted together by setting the `y` key. | |
If you do not specify margins for all sides in the margin dictionary, the old | |
margins will remain in effect for the unset sides. To prevent this and set all | |
remaining margins to a common size, you can use the `rest` key. For example, | |
`[#set page(margin: (left: 1.5in, rest: 1in))]` will set the left margin to 1.5 | |
inches and the remaining margins to one inch. | |
### Different margins on alternating pages { #alternating-margins } | |
Sometimes, you'll need to alternate horizontal margins for even and odd pages, | |
for example, to have more room towards the spine of a book than on the outsides | |
of its pages. Typst keeps track of whether a page is to the left or right of the | |
binding. You can use this information and set the `inside` or `outside` keys of | |
the margin dictionary. The `inside` margin points towards the spine, and the | |
`outside` margin points towards the edge of the bound book. | |
```typ | |
#set page(margin: (inside: 2.5cm, outside: 2cm, y: 1.75cm)) | |
``` | |
Typst will assume that documents written in Left-to-Right scripts are bound on | |
the left while books written in Right-to-Left scripts are bound on the right. | |
However, you will need to change this in some cases: If your first page is | |
output by a different app, the binding is reversed from Typst's perspective. | |
Also, some books, like English-language Mangas are customarily bound on the | |
right, despite English using Left-to-Right script. To change the binding side | |
and explicitly set where the `inside` and `outside` are, set the | |
[`binding`]($page.binding) argument in the [`{page}`]($page) set rule. | |
```typ | |
// Produce a book bound on the right, | |
// even though it is set in Spanish. | |
#set text(lang: "es") | |
#set page(binding: right) | |
``` | |
If `binding` is `left`, `inside` margins will be on the left on odd pages, and | |
vice versa. | |
## Add headers and footers { #headers-and-footers } | |
Headers and footers are inserted in the top and bottom margins of every page. | |
You can add custom headers and footers or just insert a page number. | |
In case you need more than just a page number, the best way to insert a header | |
and a footer are the [`header`]($page.header) and [`footer`]($page.footer) | |
arguments of the [`{page}`]($page) set rule. You can pass any content as their | |
values: | |
```example | |
>>> #set page("a5", margin: (x: 2.5cm, y: 3cm)) | |
#set page(header: [ | |
_Lisa Strassner's Thesis_ | |
#h(1fr) | |
National Academy of Sciences | |
]) | |
#lorem(150) | |
``` | |
Headers are bottom-aligned by default so that they do not collide with the top | |
edge of the page. You can change this by wrapping your header in the | |
[`{align}`]($align) function. | |
### Different header and footer on specific pages { #specific-pages } | |
You'll need different headers and footers on some pages. For example, you may | |
not want a header and footer on the title page. The example below shows how to | |
conditionally remove the header on the first page: | |
```typ | |
>>> #set page("a5", margin: (x: 2.5cm, y: 3cm)) | |
#set page(header: context { | |
if counter(page).get().first() > 1 [ | |
_Lisa Strassner's Thesis_ | |
#h(1fr) | |
National Academy of Sciences | |
] | |
}) | |
#lorem(150) | |
``` | |
This example may look intimidating, but let's break it down: By using the | |
`{context}` keyword, we are telling Typst that the header depends on where we | |
are in the document. We then ask Typst if the page [counter] is larger than one | |
at our (context-dependent) current position. The page counter starts at one, so | |
we are skipping the header on a single page. Counters may have multiple levels. | |
This feature is used for items like headings, but the page counter will always | |
have a single level, so we can just look at the first one. | |
You can, of course, add an `else` to this example to add a different header to | |
the first page instead. | |
### Adapt headers and footers on pages with specific elements { #specific-elements } | |
The technique described in the previous section can be adapted to perform more | |
advanced tasks using Typst's labels. For example, pages with big tables could | |
omit their headers to help keep clutter down. We will mark our tables with a | |
`<big-table>` [label] and use the [query system]($query) to find out if such a | |
label exists on the current page: | |
```typ | |
>>> #set page("a5", margin: (x: 2.5cm, y: 3cm)) | |
#set page(header: context { | |
let page-counter = | |
let matches = query(<big-table>) | |
let current = counter(page).get() | |
let has-table = matches.any(m => | |
counter(page).at(m.location()) == current | |
) | |
if not has-table [ | |
_Lisa Strassner's Thesis_ | |
#h(1fr) | |
National Academy of Sciences | |
] | |
})) | |
#lorem(100) | |
#pagebreak() | |
#table( | |
columns: 2 * (1fr,), | |
[A], [B], | |
[C], [D], | |
) <big-table> | |
``` | |
Here, we query for all instances of the `<big-table>` label. We then check that | |
none of the tables are on the page at our current position. If so, we print the | |
header. This example also uses variables to be more concise. Just as above, you | |
could add an `else` to add another header instead of deleting it. | |
## Add and customize page numbers { #page-numbers } | |
Page numbers help readers keep track of and reference your document more easily. | |
The simplest way to insert page numbers is the [`numbering`]($page.numbering) | |
argument of the [`{page}`]($page) set rule. You can pass a | |
[_numbering pattern_]($numbering.numbering) string that shows how you want your | |
pages to be numbered. | |
```example | |
>>> #set page("iso-b6", margin: 1.75cm) | |
#set page(numbering: "1") | |
This is a numbered page. | |
``` | |
Above, you can check out the simplest conceivable example. It adds a single | |
Arabic page number at the center of the footer. You can specify other characters | |
than `"1"` to get other numerals. For example, `"i"` will yield lowercase Roman | |
numerals. Any character that is not interpreted as a number will be output | |
as-is. For example, put dashes around your page number by typing this: | |
```example | |
>>> #set page("iso-b6", margin: 1.75cm) | |
#set page(numbering: "— 1 —") | |
This is a — numbered — page. | |
``` | |
You can add the total number of pages by entering a second number character in | |
the string. | |
```example | |
>>> #set page("iso-b6", margin: 1.75cm) | |
#set page(numbering: "1 of 1") | |
This is one of many numbered pages. | |
``` | |
Go to the [`{numbering}` function reference]($numbering.numbering) to learn more | |
about the arguments you can pass here. | |
In case you need to right- or left-align the page number, use the | |
[`number-align`]($page.number-align) argument of the [`{page}`]($page) set rule. | |
Alternating alignment between even and odd pages is not currently supported | |
using this property. To do this, you'll need to specify a custom footer with | |
your footnote and query the page counter as described in the section on | |
conditionally omitting headers and footers. | |
### Custom footer with page numbers | |
Sometimes, you need to add other content than a page number to your footer. | |
However, once a footer is specified, the [`numbering`]($page.numbering) argument | |
of the [`{page}`]($page) set rule is ignored. This section shows you how to add | |
a custom footer with page numbers and more. | |
```example | |
>>> #set page("iso-b6", margin: 1.75cm) | |
#set page(footer: context [ | |
*American Society of Proceedings* | |
#h(1fr) | |
#counter(page).display( | |
"1/1", | |
both: true, | |
) | |
]) | |
This page has a custom footer. | |
``` | |
First, we add some strongly emphasized text on the left and add free space to | |
fill the line. Then, we call `counter(page)` to retrieve the page counter and | |
use its `display` function to show its current value. We also set `both` to | |
`{true}` so that our numbering pattern applies to the current _and_ final page | |
number. | |
We can also get more creative with the page number. For example, let's insert a | |
circle for each page. | |
```example | |
>>> #set page("iso-b6", margin: 1.75cm) | |
#set page(footer: context [ | |
*Fun Typography Club* | |
#h(1fr) | |
#let (num,) = counter(page).get() | |
#let circles = num * ( | |
box(circle( | |
radius: 2pt, | |
fill: navy, | |
)), | |
) | |
#box( | |
inset: (bottom: 1pt), | |
circles.join(h(1pt)) | |
) | |
]) | |
This page has a custom footer. | |
``` | |
In this example, we use the number of pages to create an array of | |
[circles]($circle). The circles are wrapped in a [box] so they can all appear on | |
the same line because they are blocks and would otherwise create paragraph | |
breaks. The length of this [array] depends on the current page number. | |
We then insert the circles at the right side of the footer, with 1pt of space | |
between them. The join method of an array will attempt to | |
[_join_]($scripting/#blocks) the different values of an array into a single | |
value, interspersed with its argument. In our case, we get a single content | |
value with circles and spaces between them that we can use with the align | |
function. Finally, we use another box to ensure that the text and the circles | |
can share a line and use the [`inset` argument]($box.inset) to raise the circles | |
a bit so they line up nicely with the text. | |
### Reset the page number and skip pages { #skip-pages } | |
Do you, at some point in your document, need to reset the page number? Maybe you | |
want to start with the first page only after the title page. Or maybe you need | |
to skip a few page numbers because you will insert pages into the final printed | |
product. | |
The right way to modify the page number is to manipulate the page [counter]. The | |
simplest manipulation is to set the counter back to 1. | |
```typ | |
#counter(page).update(1) | |
``` | |
This line will reset the page counter back to one. It should be placed at the | |
start of a page because it will otherwise create a page break. You can also | |
update the counter given its previous value by passing a function: | |
```typ | |
#counter(page).update(n => n + 5) | |
``` | |
In this example, we skip five pages. `n` is the current value of the page | |
counter and `n + 5` is the return value of our function. | |
In case you need to retrieve the actual page number instead of the value of the | |
page counter, you can use the [`page`]($location.page) method on the return | |
value of the [`here`] function: | |
```example | |
#counter(page).update(n => n + 5) | |
// This returns one even though the | |
// page counter was incremented by 5. | |
#context here().page() | |
``` | |
You can also obtain the page numbering pattern from the location returned by | |
`here` with the [`page-numbering`]($location.page-numbering) method. | |
## Add columns { #columns } | |
Add columns to your document to fit more on a page while maintaining legible | |
line lengths. Columns are vertical blocks of text which are separated by some | |
whitespace. This space is called the gutter. | |
To lay out your content in columns, just specify the desired number of columns | |
in a [`{page}`]($page.columns) set rule. To adjust the amount of space between | |
the columns, add a set rule on the [`columns` function]($columns), specifying | |
the `gutter` parameter. | |
```example | |
>>> #set page(height: 120pt) | |
#set page(columns: 2) | |
#set columns(gutter: 12pt) | |
#lorem(30) | |
``` | |
Very commonly, scientific papers have a single-column title and abstract, while | |
the main body is set in two-columns. To achieve this effect, Typst's [`place` | |
function]($place) can temporarily escape the two-column layout by specifying | |
`{float: true}` and `{scope: "parent"}`: | |
```example:single | |
>>> #set page(height: 180pt) | |
#set page(columns: 2) | |
#set par(justify: true) | |
#place( | |
top + center, | |
float: true, | |
scope: "parent", | |
text(1.4em, weight: "bold")[ | |
Impacts of Odobenidae | |
], | |
) | |
== About seals in the wild | |
#lorem(80) | |
``` | |
_Floating placement_ refers to elements being pushed to the top or bottom of the | |
column or page, with the remaining content flowing in between. It is also | |
frequently used for [figures]($figure.placement). | |
### Use columns anywhere in your document { #columns-anywhere } | |
To create columns within a nested layout, e.g. within a rectangle, you can use | |
the [`columns` function]($columns) directly. However, it really should only be | |
used within nested layouts. At the page-level, the page set rule is preferable | |
because it has better interactions with things like page-level floats, | |
footnotes, and line numbers. | |
```example | |
#rect( | |
width: 6cm, | |
height: 3.5cm, | |
columns(2, gutter: 12pt)[ | |
In the dimly lit gas station, | |
a solitary taxi stood silently, | |
its yellow paint fading with | |
time. Its windows were dark, | |
its engine idle, and its tires | |
rested on the cold concrete. | |
] | |
) | |
``` | |
### Balanced columns | |
If the columns on the last page of a document differ greatly in length, they may | |
create a lopsided and unappealing layout. That's why typographers will often | |
equalize the length of columns on the last page. This effect is called balancing | |
columns. Typst cannot yet balance columns automatically. However, you can | |
balance columns manually by placing [`[#colbreak()]`]($colbreak) at an | |
appropriate spot in your markup, creating the desired column break manually. | |
## One-off modifications | |
You do not need to override your page settings if you need to insert a single | |
page with a different setup. For example, you may want to insert a page that's | |
flipped to landscape to insert a big table or change the margin and columns for | |
your title page. In this case, you can call [`{page}`]($page) as a function with | |
your content as an argument and the overrides as the other arguments. This will | |
insert enough new pages with your overridden settings to place your content on | |
them. Typst will revert to the page settings from the set rule after the call. | |
```example | |
>>> #set page("a6") | |
#page(flipped: true)[ | |
= Multiplication table | |
#table( | |
columns: 5 * (1fr,), | |
..for x in range(1, 10) { | |
for y in range(1, 6) { | |
(str(x*y),) | |
} | |
} | |
) | |
] | |
``` | |