by David Pardue (kalwisti)
I have been experimenting off and on with Typst for six months, using it increasingly for my personal projects: letters, notes, and converting my old thesis. While learning new software, I find it more productive to adopt a hands-on, practical approach. I start writing something; when I get stuck, I turn to Typst's thorough documentation, their new forum, the Typst subreddit, or a general DuckDuckGo search. This technique almost always leads to a solution.
I was inspired by a Spanish-language manual, Typst: Primeros pasos ['Typst: First Steps'], written by ToniGL68. He chose a cookbook-type format with numerous examples rather than focusing on theory. I would like to share some of his “recipes” with you, as well as offer intermediate-level tips of my own. I hope these examples will help you use Typst more efficiently; they should work both within Typst's web app and with the Typst compiler installed locally.
Document Language
Typst's text function has a parameter called lang, with which you can set the language for the entire document or parts of the document. There is also a region parameter which uses these ISO codes.
Although the default is English, if a portion of your document is in Spanish, you can type:
#set text(lang:”es”, region: “eu”)
to configure Typst for Spanish-language use with “European Union” as the region. (Adjust the region code as necessary, e.g., “es” [Spain], “mx” [Mexico].)
To revert to English, just type
#set text(lang: “eng”)
above the next English-language section.
The lang parameter allows the correct hyphenation pattern to be used, as well as the appropriate smart quotes, as shown below with the guillemets (also known as “comillas españolas” ['Spanish quotation marks']):

Switching to a Non-Roman Script
Typst supports Unicode out of the box. So if you need to use a non-Roman script in your document, you can copy and paste that script into your text (provided that the appropriate font is installed on your system, and it supports the script for that specific language).
This technique works well for inserting snippets of a non-Roman script into your document, as we see with the Sanskrit shloka below:
#set text(font: “Kalimati”)

#set text(font: “Liberation Serif”)
Shloka from the Bhagavad Gita (Chapter 2, verse 63)
“Anger leads to clouding of judgment, which results in bewilderment of memory. When memory is bewildered, the intellect gets destroyed; and when the intellect is destroyed, one is ruined.”
Produces this output:

Note: After inserting the Sanskrit snippet, remember to switch back to your Roman-alphabet font with the code
#set text(font: "Liberation Serif")
As a second example, here is the Ancient Greek "parent" of the Latin phrase quod erat demonstrandum (Q.E.D.), placed by early mathematicians at the end of mathematical proofs.
#set text(font: "Gentium Plus")
ὅπερ ἔδει δεῖξαι (ΟΕΔ)
#set text(font: "Liberation Serif")
Quod erat demonstrandum (Q.E.D.): 'what was required to be proved'
Generates the output below:

If you work with any of the CJK (Chinese, Japanese, Korean) languages, you will be pleased to know that the recent release of Typst ver. 0.13 has taken steps to improve CJK support. The new feature allows CJK Typst users to more easily write text that mixes their native language and English. (This situation is challenging because Roman-alphabet text and Chinese text are almost always typeset with different fonts.)
Interword / Interlinear Spacing
You can directly insert interword spacing using the h (horizontal spacing) function, and interlinear spacing with the v (vertical spacing) command:
Horizontal #h(1cm) spacing.
#v(1cm)
And some vertical too!
The result is shown below:

Full-width Horizontal Rule
To create a solid line (with a thickness of two points) that spans the width of your document, type:
#line(length: 100%, stroke: 2pt)
Tables
If you need to incorporate tables into your writing, Typst is quite capable. The default table formatting is plain-Jane, but Typst's developers have been adding new features which allow users to style tables attractively.
As there are many options for creating and formatting tables, I recommend that you consult the documentation. A good starting point is Typst's "Table Guide." If you would prefer a video tutorial, the best one that I found was produced by Isaac Weintraub for his BamDone YouTube channel. He walks through the process of creating tables and demonstrates various methods of changing their format (from minute 13:01 until 34:50).
The basic table below was produced with this input:
#table(
columns: 3,
[*Product*], [*Quantity*], [*Price*],
[Rice], [1002],[3,50€],
[Artichokes (canned)], [207], [2,96€],
[Dishwasher detergent], table.cell(colspan: 2)[Out of stock],
)

Note: The merged cell in the bottom right corner was achieved with the table.cell function's colspan argument. The "2" specifies that you want your cell to span two columns.
Here is a table with the same data but formatted with a more "businesslike" appearance:
#table(
stroke: none,
columns: (3),
[*Product*], table.vline(), [*Quantity*], table.vline(start:0, end:3, stroke:1pt), [*Price*],
table.hline(),
[Rice], [1002], [3,50€],
[Artichokes (canned)], [207], [2,96€],
[Dishwasher detergent], table.cell(align: center,colspan: 2,fill: red.transparentize(70%))[Out of stock],
)

Note: "stroke: none" turns off the default strokes that differentiate between rows and columns. The table.hline element produces the horizontal line under the first row. The table.vline element produces the vertical lines after the first and second columns. With the second vertical line, the "start:0" and "end:3" arguments specify that the line begins before the first row (i.e., at the top of the table) and ends after the third row.
The arguments in the table.cell function specify this cell should span two columns, be centered and filled with red color. The transparentize argument makes a color more transparent by a given factor (70% in this example).
Captioning and Referencing Tables
In his video, Isaac Weintraub suggests using the figure function to make a clear connection between your table(s) and your document text. Wrapping a table in the figure function allows you to caption and reference your table, thereby saving you future work if you need to shift the table to a different location/section of your document. It also lets you use the figure's placement parameter to float it to the top or bottom of a page.
The code block below creates a fairly complex table listing current PCLinuxOS mirrors:
#figure(
caption: [PCLinuxOS Mirrors],
table(
stroke: 0.1pt,
columns: (6),
table.cell(fill: gray.transparentize(80%))[*Continent*], table.cell(fill: gray.transparentize(80%))[*Flag*], table.cell(fill: gray.transparentize(80%))[*Country*], table.cell(fill: gray.transparentize(80%))[*City*], table.cell(fill: gray.transparentize(80%))[*Institution / Company*], table.cell(fill: gray.transparentize(80%))[*URL (Partial)*],
table.hline(start:0, end:6, stroke:2pt),
[Asia], table.vline(start:0, stroke:1pt), image("jp.png", width: 50%), [Japan], [Nomi], [JAIST (Japan Advanced Inst. of Science and Technology)], [ftp.jaist.ac.jp],
[Asia], image("sg.png", width: 50%), [Singapore], [Singapore], [Freedif Open Source Mirror], [mirror.freedif.org],
[Australia], image("au.png", width: 50%), [Australia], [ ], [AARNet], [mirror.aarnet.\ edu.au],
[Australia], image("au.png", width: 50%), [Australia], [Adelaide (?)], [Internode], [mirror.internode.\ on.net],
[Europe], image("bg.png", width: 50%), [Bulgaria], [Ruse], ["Angel Kanchev" University of Ruse], [mirrors.uni-ruse.bg],
[Europe], image("fr.png", width: 50%), [France], [Paris], [Institut Pierre-Simon Laplace], [distrib-coffee.\ ipsl.jussieu.fr],
[Europe], image("de.png", width: 50%), [Germany], [Erlangen-Nürnberg], [Friedrich-Alexander-Universität], [ftp.fau.de],
[Europe], image("gr.png", width: 50%), [Greece], [Rethymno], [University of Crete Computer Center], [ftp.cc.uoc.gr],
[Europe], image("nl.png", width: 50%), [The Netherlands], [Amsterdam], [We Are Triple], [pclinuxos.mirror. \ wearetriple.com],
[Europe], image("nl.png", width: 50%), [The Netherlands], [Ede (?)], [NLUUG (The Netherlands Local Unix User Group)], [ftp.nluug.nl],
[North America], image("us.png", width: 50%), [USA], [Princeton, New Jersey], [Princeton University], [mirror.math.\ princeton.edu],
[South America], image("br.png", width: 50%), [Brazil], [Curitiba], [Universidade Federal do Paraná], [pclinuxos.c3sl.\ ufpr.br],
[(Worldwide)], image("united-nations.png", width: 50%), [ ], [ ], [ ], [mirrors.cicku.me],
)
)<pclos-mirrors>

Note: The "caption: [PCLinuxOS Mirrors]" will add the caption as a named argument below the table. I filled the top row's cells with a partially transparent gray color. The flag icons were imported/stored in my project folder in VSCodium. The table.vline element produces a vertical line after the first column.
I gave this table a label—in angle brackets (<pclos-mirrors>). The label tells Typst to remember this element and make it referenceable under this name throughout your document. You can then refer to it in your text by typing: " @pclos-mirrors ". Typst will print a nicely formatted reference, and automatically update the label if the table's number changes.
Thus typing
Currently available PCLinuxOS mirrors are shown in @pclos-mirrors.
produces:

In the output, "Table 1" is a hyperlink; clicking on it will take you directly to that table.
Inserting Table from CSV File
Typst has a cool function called csv which creates a table by reading structured data from a .csv file. The simple table below was automatically generated by Typst when I imported the file baseball-catchers-list.csv into my project folder, using this syntax:
#table(
//stroke: none,
columns: 5,
..csv("baseball-catchers-list.csv").flatten(),
)
(Note: The “//” comments out the second line of that code block. This means the table will have the default stroke lines, black with 1pt thickness.)

Two Images Side by Side Using #figure( ) and #grid( )
The next example will illustrate how to place two images side by side, using the grid function—and wrapping it in the figure function.
Although the grid function is somewhat similar to the table function, they are intended for different uses. While the table element is intended for presenting data, the grid element is intended for presentation and layout purposes (i.e., dividing the workspace into cells/blocks). Within each grid's cell, you may add elements such as text, lists, tables, images, etc. Each cell may be formatted independently, if you wish.
The two MLB teams below have a historically intense rivalry:
#figure(
caption: [A traditional MLB rivalry],
grid(
columns: (45%,1fr,45%),
row-gutter: 0.5em,
[#image("ny-yankees-logo.png",height:2cm)],
[],
[#image("bos-red-sox-logo.png",height:2cm)],
[New York Yankees], [], [Boston Red Sox]
)
)<fig-baseball-logos>
The source code above produces this output:

Note: A grid's sizing is determined by what the Typst developers call "track size" (which is specified in the argument). In this example, the argument
columns: (45%,1fr,45%)
specifies that Column 1 (Yankees logo) and Column 3 (Red Sox logo) are each 45% of the total length. The "1fr" is a "fractional length" which means that once all the other tracks have been sized, the remaining space will be divided among the fractional tracks according to their fractions.
The layout shown in the screenshot above has three columns and two rows. There is a gutter of 0.5em
row-gutter: 0.5em
separating the two rows. The caption was created with
caption: [A traditional MLB rivalry]
One handy trick that can help us visualize the grid layout is to use Typst's rect (rectangle) function. This—together with a gray fill—will emphasize the area of cells we are working with. Using that technique with our current example, we will see the following layout:

The code snippet to generate that output is:
// We use `rect` to emphasize the
// area of cells.
#set rect(
inset: 8pt,
fill: rgb("e4e5ea"),
width: 100%,
)
#figure(
caption: [A traditional MLB rivalry],
grid(
columns: (45%,1fr,45%),
row-gutter: 0.5em,
rect[Yankees logo (image)],
[[1fr]],
rect[Red Sox logo (image)],
rect[TeamName: New York Yankees (text)], [[1fr]], rect[TeamName: Boston Red Sox (text)]
)
)<fig-baseball-logos>
Thanks to Typst's fast, incremental compilation, it is easy to make on-the-fly adjustments to your table/grid settings and fiddle until you achieve a visually pleasing result. For instance, I experimented with changing the columns setting to
columns: (auto, auto, auto)
but I felt that the images were too closely spaced together:

Boxes and Rectangles
Typst's box function allows you to create "boxes" within text; you can apply a specific format or even include content inside (such as a small image).
If you see this icon #box(fill:none,height: 15pt,image("you-are-here-icon.png")) you can be sure that "You Are Here".

The rect (rectangle) function, like the box function, allows you to create a rectangle in which you may include elements, borders, fill, auto-sizing of content, etc. but it creates a separate "block" rather than being inserted into the current line:
#rect[This is a text box.]
produces this output:

You can fill the rectangle with a background color:
#rect(fill: luma(240))[Highlighted text ...#lorem(10)]

You can apply borders, radii, dimensions, centering, as well as configuring color transparency with the "transparentize(%)" operation. The snippet below generates an understated callout box:
#align(center)[#rect(fill: blue.transparentize(90%),stroke: 0.2pt, radius: 3pt, width: 70%)[#align(left)[Sample text ...#lorem(30)]]]

A Basic Letter
Although there are several letter templates available via Typst Universe, if you just need a simply formatted letter, you can create one quickly using Typst's out-of-the-box functionality. BamDone demonstrates how to compose a letter in thirty seconds.
#set page(
paper: "us-letter",
margin: (x:0.75in, y:1in)
)
#set text(
font: "Liberation Serif",
)
#grid(
columns: (1fr,auto),
[],align(left)[
Sender Name\
Sender Address\
City, State, ZIP code\
Phone Number\
#link("mailto:John.Doe@aol.com")
]
)
Recipient Name\
Recipient Address\
City, State, ZIP code\
Dear Recipient Name,
I am writing to request …
I would also like to say these things …
In closing, I will restate my main point ...
(This letter uses no external packages --- only what Typst provides by default.)
Sincerely,\
#text(font: "Impact", size:18pt)[The Author]
First Name Last Name\
Impressive Title of Position\
Company / Institution
When you finish filling out this template with appropriate information, the letter's format will look something like the screenshot below:

Additional Resources
If you have a reading knowledge of Spanish, the manual by ToniGL68 is an excellent resource. His introduction to Typst adopts a cookbook-type format with numerous examples. The guide includes source-code "recipes" that you can work through while learning more about Typst.
Introduction to Typst
Typst: Primeros pasos. ['Typst: First Steps']. ver. 2.0. 24 Jan. 2025.
If you are signed in to Typst's web app, you can access Toni's project with read-only privileges:
Typst Web App
Typst Documentation. "Table Guide."
Typst Documentation
BamDone [Isaac Weintraub]. "Getting Started with Typst - Some Tables (S01E06)." YouTube, 27 Jun. 2024. (35 min., 34 sec.)
Getting Started with Typst
"Composing a Letter with Typst in 30 Seconds." YouTube, 26 May 2024. (0 min., 28 sec.)
Composing a Letter with Typst in 30 Seconds
I am planning to write a continuation of this cookbook for next month's magazine issue. In the meantime, I hope this will encourage you to further explore Typst's functionality.
If you are interested in seeing a Typst-generated replica of this article, I uploaded the PDF [11 p., 497 kB] to my PCLOS Cloud account and publicly shared it from there. The document's body typeface is Source Serif Pro, and the headings use Source Sans Pro.
|