How to indent every paragraph, excluding headings?

I’m trying to indent every paragraph, but the par function includes text inside headings.

Using par(first-line-indent: ) and par(hanging-line-indent: ) yields strange results. For example, the first line after a table or a grid is never indented.

Looking through the forum to make sure this question hasn’t been asked, I found that typst’s paragraph handling seems to be very inconsistent. Will I regret using it if I care a lot about things like paragraph styling?

This doesn’t answer your question of will you regret using the native styling, but if you want “fully” indented paragraphs see if something like this works for you:

#show par: it => [
  #set align(right)
  #let indent-amt = 1em
  #block(width: 100% - indent-amt)[
    #set align(left)
    #it
  ]
]

Some styling rules might not work as they did before because you are now inside a block instead of a par, but it does e.g. indent after tables

This also affects text inside tables and grids, making them look like this

image

EDIT (2025-11-02): This answer is now outdated, as there is now a native solution, as of Typst 0.13.0. Check the answer below for more information.


OLD ANSWER:

There is extensive discussion about this particular request here: Behavior of first line indentation in paragraphs seems limiting · Issue #311 · typst/typst · GitHub

Note that indenting only consecutive paragraphs - the default style - is a common style in multiple parts of the world. However, Typst doesn’t currently natively offer the alternative style of indenting all paragraphs, which is also often used, precisely due to the problem you’re describing: text inside headings is also inside a paragraph.

A fix for this is coming soon, in a future update. In the meanwhile, the issue I linked has some suggested workarounds.

Here’s how you could apply the workaround suggested at Behavior of first line indentation in paragraphs seems limiting · Issue #311 · typst/typst · GitHub :

#set par(first-line-indent: 1.5em)

#show heading: it => {
    it
    ""
    context v(-par.spacing - measure("").height)
}

= Hello world

#lorem(20)

#lorem(20)

To also apply this workaround to grids, tables, figures, and whichever other elements you need, you can simply replace #show heading with #show selector.or(heading, table, /* and more */):

#set par(first-line-indent: 1.5em)
#show selector.or(heading, table, grid, figure): it => {
    it
    ""
    context v(-par.spacing - measure("").height)
}

#table(columns: 2, [a], [b], [c], [d])

#lorem(10)

#figure(table(columns: 2, [a], [b], [c], [d]), caption: [A table])

#lorem(10)

We hope this won’t be necessary anymore in a future update. :slight_smile:

2 Likes

This is now available natively with the first-line-indent option, as of Typst 0.13, and should be used instead of the previously listed workarounds:

// Use 'all: true' to indent the first paragraph after
// headings and grids as well
#set par(first-line-indent: (amount: 2em, all: true))

= My heading

This is the first paragraph! It is indented too! #lorem(20)

This is the second paragraph! It is also indented. #lorem(20)

See more information at the docs: Paragraph Function – Typst Documentation