Skip to content

typst a new better latex?

I stumbled upon new typesetting software typst. I claims to be better than latex. Hmm… steep. I decided to take peek how they compare.

Let’s break it down from the ground up.

1. What is Typst?

  • Goal: Typst aims to be as powerful as LaTeX for complex typesetting but much easier to learn and use, drawing inspiration from Markdown’s simplicity for common tasks. It’s designed for speed, ease, and versatility.
  • Core Idea: It’s a markup-based system like LaTeX. You write text in a file and use special syntax (markup) and commands (functions) to format it. Then, you compile this file into a PDF. My main gripe: why = for headings? In markdown I use # so I have to refactor my all texts. # is a function symbol…
  • Key Differences from LaTeX:
    • Simpler Syntax: Uses more dedicated syntax (often like Markdown) for common things (bold, italics, lists, headings). Is it? I use * and _ as well and compile to latex via pandoc.
    • Consistent Functions: Commands (called functions in Typst) work more consistently than LaTeX commands/packages. Are they? Latex function are versatile. Only problem there is to include packages.
    • Faster Compilation: Typst compiles much faster (often milliseconds vs. seconds), enabling instant previews.
    • Integrated Scripting: Has a built-in, readable scripting language for complex logic, automation, and custom functions. This is a major advantage over LaTeX’s macro system.
    • Modern Features: Includes features like built-in bibliography management. It’s written in Rust and is a completely separate system from TeX/LaTeX. Rust is cool huh? If somithing is rust can’t be wrong. And if they claim it’s modern, oooooooh modern is so cool.

2. Getting Started

  • Web App: The easiest way to start is using the free online web app at typst.app. It offers an editor with syntax highlighting, autocompletion, and instant preview.
  • Local Installation (CLI): You can install the Typst compiler locally on Mac, Windows, or Linux.
    • On macOS: brew install typst.
    • On Windows: winget install --id Typst.Typst.
    • Via Rust’s package manager (if you have Rust installed): cargo install typst-cli.
    • Pre-built binaries are also available on GitHub.
  • Basic Workflow:
    1. Create a text file with a .typ extension (e.g., mydocument.typ).
    2. Write your content using Typst syntax.
    3. Compile it using the command line: typst compile mydocument.typ. This creates mydocument.pdf.
    4. For live preview while editing: typst watch mydocument.typ. This recompiles automatically when you save the .typ file.

3. Basic Syntax (Markup Mode)

Typst starts in "markup mode," which feels familiar if you know Markdown.

  • Paragraphs: Just type text. Separate paragraphs with a blank line.
  • Bold: *bold text*.
  • Italics (Emphasis): _italic text_.
  • Headings: Use = signs. One = for level 1, == for level 2, etc.. (e.g., = Section Title, == Subsection Title).
  • Lists:
    • Unordered: Start lines with - or *.
    • Ordered: Start lines with + or 1.. Indent for sublists.
  • Links: #link("URL")[Displayed Text].
  • Code:
    • Inline: `code`.
    • Block: or language for syntax highlighting.
  • Comments: // Single line comment or /* Multi-line comment */.
  • Line Breaks: Use \ for a forced line break.

4. Functions and Code Mode (The LaTeX-like Power)

This is where Typst goes beyond Markdown. You use functions for more complex formatting, layout, and logic.

  • Function Calls: Start with #. Syntax is generally #function_name(argument1, argument2)[content].
    • argument1, argument2: These are positional or named arguments passed in parentheses. Named arguments look like name: value.
    • [content]: This is a content block, where you can put more markup that the function operates on.
  • Examples:
    • #underline[some text]
    • #text(red)[colored text]
    • #image("path/to/image.png", width: 80%)
    • #figure(image("path/to/image.png"), caption: [Figure caption])
    • #table(columns: 4, stroke: none, [Cell 1], [Cell 2], ...) (See docs for table syntax)
  • Code Mode: You can embed scripting logic directly using # followed by an expression, or within code blocks { ... }. Content blocks [...] let you embed markup within code.

5. Math Mode (Familiar from LaTeX)

Typst has excellent math support, very similar to LaTeX but often cleaner.

  • Inline Math: $ x + y = z $.
  • Display Math (Block): $ a^2 + b^2 = c^2 $ (Note the spaces inside the $). Block equations are centered by default.
  • Syntax:
    • Use standard symbols: +, -, =, <, >.
    • Superscript: ^ (e.g., x^2).
    • Subscript: _ (e.g., a_1).
    • Fractions: frac(a, b) or often just a / b works.
    • Symbols: alpha, beta, pi, sum, sqrt, in, RR etc. Often without backslashes. Use quotes for multi-letter variables like $ "area" $.
    • Functions: sin, cos, log, exp. Typst recognizes many automatically.
    • Matrices/Vectors: #vec(a, b), #mat(1, 2; 3, 4).
    • Alignment: Use & for alignment points in multi-line equations (within $...$ block).
  • Equation Numbering: Use #set math.equation(numbering: "(1)") or similar patterns.
  • Labels & References: Use <label> to label an equation (e.g., $ E = mc^2 $ <lorentz>) and @label to refer to it (e.g., As shown in @lorentz...).

6. Layout and Styling

  • Setting Rules: Use #set <element>(parameter: value) to apply styles globally (like preamble commands in LaTeX).
    • #set page(width: auto, height: auto, margin: (left: 2cm, right: 2cm, top: 2cm, bottom: 2cm)).
    • #set text(font: "New Computer Modern", size: 12pt).
    • #set heading(numbering: "1.a").
  • Show Rules: Use #show <element>: <transformation> to completely redefine how elements are displayed. This is more advanced but very powerful.
    • #show link: underline.

7. Scripting (Typst’s Superpower)

You can use variables, functions, loops, and conditionals directly in your document.

  • Variables: #let name = "Typst" Define a variable. Use it later: Hello, #name!.
  • Conditionals: #if condition { // content if true } else { // content if false }.
  • Loops: #for item in array { // content for each item }.
  • Data Structures: Supports arrays, dictionaries, strings, integers, floats, etc..
  • Functions: #let my_function(arg) = { // code or content using arg }

8. Templates and Packages

  • Includes: #include "filename.typ" simply inserts the content of another file (like LaTeX’s \input).
  • Imports: #import "module.typ": function1, variable2 or #import "module.typ" brings in functions and variables from another file, often used for creating reusable templates or libraries (similar to LaTeX packages). You access imported items using module_name.item_name if not imported directly.
  • Universe: Typst has a package/template system called "Universe" where you can find and share templates. You can initialize a project with a template using typst init @preview/template-name.

Summary: Typst vs Markdown vs LaTeX

  • Simplicity: Typst basic syntax is close to Markdown, making simple documents easy. Much simpler than LaTeX for basics.
  • Power: Typst functions, styling rules, and scripting offer power comparable to or exceeding LaTeX for many tasks, often with cleaner syntax. Math is LaTeX-like.
  • Consistency: Typst aims for a more consistent syntax and system design compared to LaTeX’s sometimes disparate package conventions.
  • Speed: Dramatically faster compilation than LaTeX.
  • Scripting: Integrated scripting is a major advantage over standard LaTeX.
  • Ecosystem: LaTeX has a vast, mature ecosystem of packages. Typst’s is newer but growing rapidly. Some complex LaTeX features (like TikZ for graphics) might not have direct equivalents yet, though alternatives are emerging.

Next Steps:

  1. Try the Typst Web App (typst.app).
  2. Work through the Official Tutorial.
  3. Consult the Official Documentation for reference.
  4. Explore the Guide for LaTeX Users in the Typst docs.
  5. Look at examples on Typst Universe or Awesome Typst.

Typst has a learning curve, especially the scripting part, but coming from LaTeX and Markdown, you should find many concepts familiar and appreciate the blend of simplicity and power. Good luck!

Search Sources

Okay, let’s structure a book project in Typst, leveraging external files for organization. This mirrors how you might structure a large LaTeX project using \include or \input.

Here’s a typical directory structure and the contents of the main file (main.typ) and the included files.

1. Project Directory Structure:

my-book/
├── main.typ          # The main document file that includes everything
├── bibliography.bib  # Your bibliography data (BibTeX format)
│
├── frontmatter/
│   ├── 00-half-title.typ
│   ├── 01-copyright.typ
│   ├── 02-dedication.typ
│
├── body/
│   ├── 00-introduction.typ
│   │
│   ├── part1/
│   │   ├── 01-chapter1.typ
│   │   ├── 02-chapter2.typ
│   │
│   ├── part2/
│   │   ├── 03-chapter3.typ
│   │   ├── 04-chapter4.typ
│
└── backmatter/
    ├── 01-appendix-a.typ
    ├── 02-appendix-b.typ
  • Numbering the files (e.g., 00-, 01-) isn’t strictly necessary for Typst but helps maintain order in the file system.
  • Using separate directories (frontmatter, body, backmatter, part1, part2) improves organization.

2. Main Document (main.typ)

This file sets up the document structure, includes the content files, and generates elements like the Table of Contents and Bibliography.

// main.typ
// --- Document Setup ---
#set document(title: "My Awesome Book", author: "Your Name")
#set page(
  paper: "a5", // Or "us-letter", "a4", etc.
  margin: (x: 2.5cm, top: 3cm, bottom: 2.5cm),
  numbering: "1" // Start with Arabic numerals by default
)
#set text(font: "New Computer Modern", size: 11pt, lang: "en") // Or specify your preferred font

// --- Heading Setup ---
// Customize how headings look and are numbered
#set heading(numbering: "1.1") // Chapter.Section numbering
// Part numbering (often Roman numerals) - Requires slightly more setup
// Simple way: Use unnumbered top-level headings for Parts in main.typ
// Advanced way: use show rules or #counter for custom Part numbering (see Typst docs)

// --- Front Matter ---

// Switch to Roman numerals for front matter pages (optional)
#counter(page).update(1)
#set page(numbering: "i")

// Half Title Page (often simple, centered title)
#include "frontmatter/00-half-title.typ"
#pagebreak() // Force a new page

// Title Page (could be generated here or included)
// Typst doesn't have a built-in \maketitle, you compose it.
// Example:
#page(
  // Clear page numbering for title page
  numbering: none,
  // Example simple title page layout
  align(center)[
    #v(20%) // Push content down
    #text(size: 2em, weight: "bold")[My Awesome Book]
    #v(1em)
    #text(size: 1.2em)[An Exploration]
    #v(4em)
    Your Name
    #v(1fr) // Push to bottom
    Publisher Name (Optional) \
    #datetime.today().year()
  ]
)
#pagebreak()


// Copyright Page
#include "frontmatter/01-copyright.typ"
#pagebreak() // Usually on the verso (left) of the title page

// Dedication Page (Optional)
#include "frontmatter/02-dedication.typ"
#pagebreak()

// Table of Contents
#outline(
  title: [Table of Contents], // Customize the title
  depth: 2 // Show Parts (level 1) and Chapters (level 2)
  // indent: true // Indent nested levels
)
#pagebreak()

// --- Main Matter ---

// Reset page numbering to Arabic for the main content
#counter(page).update(1)
#set page(numbering: "1")

// Introduction (often uses a distinct heading style or is unnumbered)
#include "body/00-introduction.typ"
#pagebreak() // Often start parts/chapters on new pages

// Part I
// You can put the Part heading directly here to structure the TOC
= Part I: The Beginning
#include "body/part1/01-chapter1.typ"
#pagebreak()
#include "body/part1/02-chapter2.typ"
#pagebreak()


// Part II
= Part II: Further Explorations
#include "body/part2/03-chapter3.typ"
#pagebreak()
#include "body/part2/04-chapter4.typ"
#pagebreak()


// --- Back Matter ---

// Set up numbering for Appendices (e.g., A, B, C)
#counter(heading).update(0) // Reset heading counter if needed
#set heading(numbering: "A", title: [Appendix]) // Use letter numbering for top-level headings

= Data Sets // This will be Appendix A
#include "backmatter/01-appendix-a.typ"
#pagebreak()

= Further Tools // This will be Appendix B
#include "backmatter/02-appendix-b.typ"
#pagebreak()

// Reset heading numbering if more sections follow that shouldn't be appendices
// #set heading(numbering: "1.1")


// Bibliography
// Ensure you have citations in your text like #cite(<key>) or @key
#bibliography(
  "bibliography.bib",
  title: [Bibliography] // Customize the title
  // style: "apa" // Specify a citation style if needed
)

// --- End of Document ---

3. Example Content Files:

  • frontmatter/00-half-title.typ:

    // frontmatter/00-half-title.typ
    // Usually just the title, centered vertically and horizontally
    #page(numbering: none)[ // Suppress page number for this page specifically
      #v(1fr) // Push content down vertically
      #align(center)[
        #text(size: 1.5em)[My Awesome Book]
      ]
      #v(1fr)
    ]
    
  • frontmatter/01-copyright.typ:

    // frontmatter/01-copyright.typ
    // Often placed lower on the page
    #page(numbering: none)[
      #v(2fr) // Push down more
      #align(center)[
        Copyright © #datetime.today().year() Your Name. \
        All rights reserved.
    
        ISBN: 978-X-XXXXXX-XX-X (Print) \
        ISBN: 978-X-XXXXXX-XX-X (eBook)
      ]
      #v(1fr)
    ]
    
  • frontmatter/02-dedication.typ:

    // frontmatter/02-dedication.typ
    #page(numbering: none)[
      #v(1fr)
      #align(center)[
        #text(style: "italic")[
          For someone special.
        ]
      ]
      #v(1fr)
    ]
    
  • body/00-introduction.typ:

    // body/00-introduction.typ
    // Often unnumbered, use =* or manage via #set heading(numbering: none) temporarily
    =* Introduction
    
    This book introduces... We will cover concepts such as #link("https://typst.app")[Typst] and its features. See @important-reference for details.
    
    #lorem(200) // Placeholder text
    

    (Note: Use =* Introduction for an unnumbered top-level heading)

  • body/part1/01-chapter1.typ:

    // body/part1/01-chapter1.typ
    == Chapter One: Getting Started <chap1>
    
    This is the first chapter. We begin by exploring the setup.
    
    === Installation
    
    Follow the instructions...
    
    === Basic Syntax
    
    Typst uses simple markup like *bold* and _italics_.
    
    Refer back to @chap1 later.
    

    (Note: <chap1> creates a label you can reference with @chap1)

  • backmatter/01-appendix-a.typ:

    // backmatter/01-appendix-a.typ
    // The title ("Appendix A: Data Sets") is generated by the = heading in main.typ
    // combined with the #set heading rule. You only add the content here.
    
    Here is the raw data used in Chapter 3.
    
    #figure(
      table(
        columns: (auto, 1fr),
        [Timestamp], [Value],
        [2023-01-01], [10.5],
        [2023-01-02], [11.2],
      ),
      caption: [Sample data table.]
    ) <data-table>
    
    See table @data-table.
    
  • bibliography.bib: (Standard BibTeX format)

    @book{important-reference,
      author    = {Author Name},
      title     = {The Big Book of Things},
      publisher = {Publisher House},
      year      = {2022},
    }
    
    @online{typst-app,
      author = {{Typst Contributors}},
      title  = {Typst},
      url    = {https://typst.app},
      urldate = {2024-01-01}, % Use current date when citing
    }
    

4. Compilation:

Navigate to the my-book/ directory in your terminal and run:

typst compile main.typ

This will generate main.pdf incorporating all the included files, table of contents, and bibliography. For live previews while editing, use:

typst watch main.typ

This structure provides a clean, modular way to manage a large book project in Typst, similar to LaTeX best practices but using Typst’s simpler #include and built-in features for TOC and bibliography. Remember to use citations (@key) in your text where you want references to appear in the bibliography.

Now let’s make a lettrine or casedrop

Okay, let’s create a Typst function for creating a lettrine (also known as a drop cap). This involves making the first letter of a paragraph larger and having the subsequent lines wrap around it.

We’ll define a function, let’s call it lettrine, that takes the paragraph content as an argument and allows customization for size, font style, number of lines to drop, and spacing.

// --- Lettrine Function Definition ---
#let lettrine(
  body,                 // The paragraph content goes here
  lines: 3,            // How many lines the letter should drop
  size-factor: 3.0,    // A multiplier for the letter's size (can be adjusted)
  gap: 4pt,            // Horizontal space between the letter and the text
  letter-style: (it) => { strong(it) } // Style applied to the letter (e.g., bold)
) = {
  // Ensure the input 'body' is content and has text
  assert(body.has("text"), message: "Lettrine body must contain text.")
  let text-content = body.text
  if text-content.len() == 0 { return body } // Return unchanged if empty

  // Extract the first letter and the rest of the text
  let first-letter = text-content.at(0)
  let rest-of-text = text-content.slice(1)

  // Apply the custom styling to the first letter
  let styled-letter = letter-style(text(first-letter))

  // --- Calculate the size and position ---
  // We need to scale the letter and figure out its dimensions
  // to correctly position it and allow text wrapping.

  // 1. Determine the approximate height of one line of normal text
  //    We measure a capital letter like 'X' in the current text style.
  //    The 1.2 factor approximates typical line spacing (leading).
  let approx-line-height = measure(text("X")).height * 1.2

  // 2. Calculate the target height for the drop cap based on `lines`
  let target-height = approx-line-height * lines

  // 3. Scale the styled letter to reach the target height
  //    Measure the styled letter at normal size first.
  let base-letter-size = measure(styled-letter)
  //    Calculate the scaling factor needed to achieve the target height.
  let scale-ratio = target-height / base-letter-size.height

  let scaled-letter = scale(factor: scale-ratio, styled-letter)

  // 4. Measure the final scaled letter's dimensions
  let final-metrics = measure(scaled-letter)
  let letter-width = final-metrics.width
  let letter-height = final-metrics.height // Should be close to target_height

  // 5. Create the drop cap element using `box` and `baseline` adjustment
  //    We lower the letter so its top roughly aligns with the top of the first text line.
  //    Lower by its height, then raise by roughly one line height (heuristic).
  let drop-cap = box(
    baseline: -letter-height + approx-line-height * 0.85, // Adjust baseline
    // The factor 0.85 is empirical for good top alignment, may vary slightly with font
    scaled-letter
  )

  // --- Output the Result ---
  // Place the drop cap, add the horizontal gap, then the rest of the text.
  // Typst's paragraph layout engine will handle the wrapping.
  drop-cap
  h(gap) // Add horizontal space
  rest-of-text
}

// --- Example Usage ---

#set par(justify: true) // Justify text for a typical book look

// Basic usage:
#lettrine[
  This is the first paragraph demonstrating the basic lettrine effect.
  The initial letter spans three lines, as is the default. The rest of
  the text wraps neatly around it. We can see how the layout engine
  handles the justification and line breaks alongside the larger letter.
]

// Customize lines, gap, and style:
#lettrine(
  lines: 2,
  gap: 2pt,
  letter-style: (it) => text(fill: blue, weight: "bold", it)
)[
  Another paragraph, this time the lettrine only drops two lines and
  has less gap. The letter itself is styled differently - it's blue
  and bold. This shows the flexibility of the function using the
  provided parameters. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
]

// Using a different size factor (might override 'lines' effectiveness if not careful)
#lettrine(
  size-factor: 4.0, // This is now explicitly set, might not match 3 lines exactly
  lines: 3 // Still used for baseline calculation
)[
  Here, we used a larger size-factor directly. While `lines` is still used
  for vertical alignment calculations, the raw `size-factor` directly
  influences the visual size. Depending on the font and factor, it might
  look slightly taller or shorter than exactly 3 lines. Sed do eiusmod tempor
  incididunt ut labore et dolore magna aliqua.
]

// Example with different font (if available)
#lettrine(
  letter-style: (it) => text(font: "Linux Libertine", weight: "bold", it)
)[
  If you have specific fonts installed, you can apply them to the lettrine.
  This example attempts to use Linux Libertine for the 'I'. The appearance
  heavily depends on the font's metrics and design. Ut enim ad minim veniam,
  quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
]

Explanation:

  1. Function Definition:

    • #let lettrine(...) = { ... } defines the function.
    • body: This is the main argument where you’ll put the paragraph text using square brackets ([]) or parentheses (()). Typst treats this as "content."
    • lines: 3: Default number of lines the letter drops.
    • size-factor: 3.0: Initial scaling multiplier (used if lines calculation isn’t perfect or for different effects). We refined it to calculate scale based on lines.
    • gap: 4pt: Default space between the drop cap and the text.
    • letter-style: (it) => { strong(it) }: A function to style the letter. By default, it makes the letter bold (strong). You can pass other functions, like #text(fill: red, ...) or just (it) => it for no style.
  2. Input Handling:

    • It checks if the input body is valid text content.
    • It extracts the first-letter and the rest-of-text.
  3. Styling and Scaling:

    • Applies the letter-style function.
    • Calculates the approx-line-height based on the current text settings by measuring a capital ‘X’.
    • Calculates the target-height the drop cap should occupy based on lines.
    • Calculates the necessary scale-ratio to make the letter reach that target-height.
    • Applies the scaling using scale().
  4. Positioning (The Core Trick):

    • Measures the final scaled-letter.
    • Uses a box() element for the drop cap.
    • The key is the baseline adjustment:
      • baseline: -letter-height + approx-line-height * 0.85 This effectively lowers the large letter so that its top aligns near the top of the first line of the regular text. The 0.85 factor is a common heuristic that works well for many fonts; you might occasionally fine-tune it.
  5. Output:

    • Places the adjusted drop-cap box.
    • Adds horizontal space (h(gap)).
    • Places the rest-of-text.
    • Typst’s paragraph layout automatically flows the rest-of-text around the space created by the drop-cap box and the h(gap).

This function provides a good balance of automation (calculating size based on lines) and customization. You can place this function definition at the beginning of your document or in a separate .typ file that you #import.

Now, let’s check the classic motto or citation function at the paragraph start.

Okay, creating a function for a right-aligned, italicized motto at the start of a section or paragraph is straightforward in Typst. We can use the align function and apply emphasis.

Here’s a function motto that does this:

#let motto(
  content,             // The text of the motto (passed as content)
  space-after: 1em     // Optional vertical space after the motto block
) = {
  // Align the content block to the end (right in LTR languages)
  align(end)[
    // Apply emphasis (italics) to the content
    #emph(content)
  ]
  // Add some vertical space after the motto block, if desired
  if space-after > 0pt {
    v(space-after)
  }
}

// --- Example Usage ---

// Example 1: Basic Motto
#motto[
  _Veni, vidi, vici._ // You can use Markdown italics inside too
]

This paragraph follows the first motto. Notice the default spacing provided by `1em`.

// Example 2: Motto with specific text and less spacing
#motto(
  content: [Ars longa, vita brevis.],
  space-after: 0.5em
)

This paragraph follows the second motto, with less space after it.

// Example 3: Longer motto showing wrapping
#motto[
  It is not the critic who counts; not the man who points out how the strong man stumbles, or where the doer of deeds could have done them better.
]

This main paragraph text follows the longer motto. The motto text itself wraps within its right-aligned block and remains italicized.

// Example 4: Motto before a chapter heading
#motto("Festina Lente.")
== Chapter One

The chapter begins here...

Explanation:

  1. #let motto(...) = { ... }: Defines a function named motto.
  2. content: The primary argument. You’ll pass the motto text here, typically using square brackets [] for content blocks, which allows multiline text and basic markup like _italics_ within the motto itself (though the function already makes it italic).
  3. space-after: 1em: An optional named argument setting the vertical space inserted after the motto block. It defaults to 1em (the width of a lowercase ‘m’ in the current font, a common vertical spacing unit). You can set it to 0pt if you want no extra space.
  4. align(end)[ ... ]: This is the core alignment command.
    • align(): Takes content and aligns it within its own block.
    • end: Aligns the content to the writing direction’s end (right for English/LTR languages, left for RTL). This is generally preferred over right for better internationalization.
  5. #emph(content): Applies emphasis (typically rendered as italics) to the content passed into the function.
  6. if space-after > 0pt { v(space-after) }: Checks if the user wants any space and inserts vertical space using v(). This prevents adding zero space if space-after is set to 0pt.

You can place this function definition at the top of your main Typst file or in a separate .typ file that you import using #import. Then, simply call #motto[...] wherever you need it.

Conclusion

It is as complicated as latex but very fresh. I doubt I would use it in serious project, but if you don’t know latex it can be better choice.

Happy typesetting!

  • #typesetting
  • #typst
  • #latex
  • #comparison
  • #markup-language
  • #documentation
  • #syntax
  • #scripting
  • #templates
  • #web-app
  • #CLI
  • #math-mode
  • #layout
  • #styling
  • #lettrine
  • #motto

Leave a Reply

Your email address will not be published. Required fields are marked *