# typstyle: a Typst formatter that refuses to touch your prose

> typstyle is an Apache-2.0 licensed Rust formatter for Typst documents, published on crates.io and npm with a WebAssembly build, a browser playground and pre-commit and GitHub Action integrations. It suits Typst authors who want machine-consistent layout without a formatter that reflows their text, and its stated design goals include convergence and not changing the rendered output.

**typstyle-rs/typstyle** — Beautiful and reliable typst code formatter

- Repository: https://github.com/typstyle-rs/typstyle
- Website: https://typstyle-rs.github.io/typstyle/
- Stars: 902 · Forks: 34
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/typstyle-rs-typstyle

## Four design goals, and the second one does the work

typstyle is a code formatter for Typst, and the README states its design goals as a numbered list, which is unusually direct for a formatter.

The first is opinionated. The project wants a consistent style across all codebases, so typstyle is not a tool you configure into your own house style. It is a tool that decides.

The second is code only: the formatter wants to format only the code, and contents should be left untouched as much as possible. This is the goal that separates typstyle from most text formatters, and it runs through everything else in the project. Text wrapping is a mode you opt into rather than the default, and the whitespace preservation rules exist for the same reason.

The third is convergence: running the formatter twice should not change the code. Any formatter that is not idempotent breaks the model where the formatted output is the canonical form, and it breaks every CI check that formats and then compares.

The fourth is correctness: the formatter should not change the looking of the rendered output. This is the constraint that rules out the tempting reformattings, because a Typst document is markup with semantic meaning in it, and a change that looks cosmetic in the source can change the page.

Those four goals together explain the tool's shape. It is a layout engine for code, not for prose, and it is deliberately conservative about touching anything it does not fully understand.

## What it refuses to reformat, and the escape hatch

The consequences of the code only goal are visible in the specific rules.

Text wrapping is off by default. The --wrap-text option takes none, which is the default, fill to wrap to the line width, sentence for one per line, or fill-sentence for both. So a document that has been hand-wrapped is left wrapped, and a document that has not been wrapped is not wrapped either, unless you ask. That is the opposite of what a prose formatter does.

Whitespace is preserved in the places where it carries meaning. Typstyle preserves trailing spaces and whitespace-only lines inside multiline strings, raw blocks, and format-disabled source, and the reason given is that these spaces can affect content, so the formatter does not trim every output line. Generated indentation and flow spacing avoid adding trailing whitespace of their own. The line-ending rules are the same idea applied to breaks: all policies preserve newlines in strings, verbatim raw spans and format-disabled source, while raw-block boundary breaks follow the layout policy.

There is a deliberate escape hatch, and it is per-node rather than per-file. If typstyle is not working as expected, you can use // @typstyle off or /* @typstyle off */ to disable the formatter on the next node of code.

And then there is the part that is easy to miss: typstyle also gives up formatting part of the code if it is not able to format it correctly, and prints that part as is. That happens if the part contains a syntax error, or contains syntaxes that are not supported by the formatter. So a document with a construct typstyle does not handle will be left alone rather than mangled, and the cost is that the formatting is silently partial. The README asks you to report those cases as issues on the GitHub repository.

For a tool whose selling point is correctness, that failure mode is a defensible choice. The alternative, refusing to format a document it does not fully understand, would be more annoying and no safer.

## The CLI, its check mode, and the line-ending policy

The command line interface is the primary surface, and the help output in the README is worth reading as a specification of the tool's scope.

The usage is typstyle [OPTIONS] [INPUT]..., where the input is a list of files or directories to format, defaulting to stdin. The mode flags are the interesting part. The --check mode exits with 0 if the input is formatted correctly and with a non-zero status code if formatting is required, and --diff runs in diff mode, showing a unified diff of what formatting changes would be made, with the same exit code contract. The --inplace flag formats the file in place.

That trio is the standard formatter shape, and the two non-destructive modes are what make it usable in CI. --check is the one you put in a pipeline.

Under Format Configuration there are four options. The line width is settable with -l or --line-width, defaulting to 80, with the alias column and the short alias c. The indent width is -t or --indent-width, defaulting to 2, aliased as tab-width. There is a --no-reorder-import-items flag to disable alphabetical reordering of import items, which tells you the default is to sort them. And there is the --wrap-text option described above.

The line-ending policy is the third option group and the one with the most nuance. The --line-ending option takes lf, crlf or preserve, and preserve is the default. The rule is specific: preserve generates CRLF breaks when the input contains at least one CRLF and has no bare LF or bare CR, and mixed endings, bare CR, and inputs without newlines fall back to LF. The README also says that range formatting detects against the entire original document, and that to enforce LF in CI you should use typstyle --check --line-ending=lf explicitly. That last sentence is the practical takeaway, because the default preserves rather than enforces, and a repository that expects LF in version control should say so.

The debug options are for when the formatter is not doing what you expect. The --ast flag prints the AST of the input file and --pretty-doc prints the pretty document, and --timing shows the elapsed time taken by the formatter. The --verbose and --quiet flags set the log level.

## Installing it, and the four ways to consume it

There are four install routes and several ways to use it, and the README is explicit that trying it in a browser comes first.

The playground is the first recommendation. It runs the latest version of typstyle with live formatting, and the README calls it perfect for testing formatting behavior before installing locally. There is also a note that the playground integrates the latest version of typstyle, and that if you encounter formatting issues you should verify them in the playground first before reporting bugs. That is a sensible triage rule, because the playground runs current code while your local binary may be older.

For the CLI, the install options are: download the binary from the release page, install it from your package manager via the Repology listing, notably in the Archlinux CN repo, install it using cargo-binstall:

```sh
cargo binstall typstyle
```

or install it with cargo:

```sh
cargo install typstyle --locked
```

The --locked flag is the detail to notice. It installs the dependency versions recorded in the lock file rather than resolving fresh ones, which is what you want for a formatter whose output is part of your build.

The four ways to use it are the CLI, the editor, the web app and a library. For the editor, typstyle has been integrated into tinymist, and you use it by installing the tinymist plugin and setting tinymist.formatterMode to typstyle. For the web app, there is the typstyle-tampermonkey userscript that integrates typstyle into the typst.app editor, and the playground itself.

As a library there are two builds. The Rust one is typstyle-core on crates.io, and the WebAssembly one is @typstyle/typstyle-wasm-bundler on npm, which is the package the playground runs.

For CI, the typstyle-action repository, maintained by @grayespinoza, can install and run typstyle in a GitHub Action. For pre-commit, the hook configuration is short:

```yaml
  - repo: https://github.com/typstyle-rs/pre-commit-typstyle
    rev: ''  # The revision or tag you want to use
    hooks:
      - id: typstyle
```

The empty rev is a placeholder for you to fill in, and the README does not say which revision to use, so pinning the version you actually have installed is on you.

## A workspace built on the real Typst crates, not a parser of its own

The Cargo.toml explains more about the implementation than the README does, and it is a reassuring picture.

The workspace has members of crates/* and tests, with crates/typstyle as the default member, and the version is 0.15.2 with rust-version 1.92 and edition 2024. The licence is Apache-2.0.

The important detail is in the workspace dependencies, where typstyle pins the actual Typst libraries rather than parsing the format itself: typst-syntax, typst-assets, typst-render and typst-layout are all at 0.15.1, and typst itself is at 0.15.1, with typst-shim at 0.15.0. The README badge for the npm wasm package and the release tags line up with this, since v0.15.2 of typstyle tracks the 0.15 series of Typst.

That matters for a formatter. If the formatter used its own parser, it would need to keep up with Typst's syntax independently and would eventually disagree with the compiler about what a document means. By building on typst-syntax, the formatter and the renderer share an understanding of the language, which is a large part of how the correctness goal is met.

There are two internal crates, typstyle-core and typstyle-consistency, and the latter is named for the convergence goal, so the idempotence check is a first-class part of the project rather than an afterthought. The core dependencies include unicode-width, which the comment explains is used to get the same version as in pretty, along with icu_segmenter for text segmentation, itertools, prettyless for pretty printing, rustc-hash, serde, smallvec and thiserror. The CLI adds clap with derive and env features, clap_complete for shell completions, path-absolutize, walkdir for directory traversal, and log with anstream and colored for output. The WASM build uses wasm-bindgen, serde-wasm-bindgen, js-sys, syn and console_error_panic_hook.

The test dependencies are worth a look too, because they say what the project considers worth testing. insta and insta-cmd are snapshot testing tools, similar-asserts handles the diffing, libtest-mimic provides a test harness, and tiny-skia is a rendering library, which is consistent with a project whose correctness claim is about the rendered output rather than the source text.

The repository root has a justfile for the developer commands, a rustfmt.toml, and a docs/ directory whose site is built with shiroa, and the justfile's generate-cli-help recipe pipes the binary's own help output into docs/assets/generated/cli-help.txt, so the CLI reference in the documentation cannot drift from the binary.

## A 0.x version line that ships every few weeks

On maintenance, the repository is not archived and the last push was on 2026-10-17, on the master branch.

The release history shows a project that ships often. The recent tags are v0.15.2 from 2026-10-12, v0.15.1 from 2026-08-02 and v0.15.0 from 2026-06-20. So three releases inside four months, with the newest one five days before this review.

The version number is the thing to read carefully. The crate is 0.15.2, which means it is pre-1.0, and in the Rust ecosystem a 0.x version carries no compatibility promise: a minor bump can break your build. For a formatter, that has a specific consequence. A formatter is normally the component of a build you trust to be stable, because every other check depends on it, and a 0.x formatter that changes its output between versions will produce diff noise in every pull request where the version moved.

The mitigation is the same as for any tool in a CI pipeline: pin it. The --locked flag on the cargo install, the fixed rev in the pre-commit configuration, and a fixed version in the GitHub Action are all in your hands, and the README's own advice to verify behaviour in the playground, which runs the latest version, tells you the latest version is the one where behaviour changes show up first.

The related question is what happens to the Typst language underneath. The workspace pins the 0.15 series of the Typst crates, so when Typst itself moves, typstyle has to move with it, and a version bump in typstyle is often the visible consequence of a Typst release. The formatter follows the language, which is the right dependency direction, and it means your formatting behaviour is coupled to your Typst version. If you upgrade Typst, expect a typstyle upgrade in the same change.

The licence is Apache-2.0, declared in the Cargo.toml and the LICENSE file at the root of the tree, and the project is distributed through crates.io, npm, Repology and a GitHub Releases archive. This review does not interpret licence terms.

The one thing the documentation does not spell out is the migration story between versions, and the CHANGELOG.md at the root of the repository is where to look for it before you bump the version in a repository that formats on every commit.

## Conclusion

typstyle fits a Typst author or a team with Typst documents in version control who wants one formatting style across the whole repository, because the formatter is opinionated, converges on a second run and does not reflow content, and the pre-commit and GitHub Action integrations make it enforceable in CI. It is the wrong choice if you want the formatter to reflow and rewrap your prose, since text wrapping is off by default and the content is deliberately left alone, and it is the wrong choice if you need it to format a document with syntax it does not support, since in that case it prints that part as is rather than failing. Before you adopt it, run the playground on a copy of your real document first, as the README advises, and set --line-ending=lf explicitly in CI if you want LF enforced rather than preserved.

## FAQ

### How do I install typstyle?

The README lists four options: download the binary from the release page, install from a package manager through Repology, use `cargo binstall typstyle`, or use `cargo install typstyle --locked`. You can also try it in the browser playground before installing anything.

### Does typstyle reformat my prose?

By default it does not. The design goal is code only, with contents left untouched as much as possible, and the --wrap-text option defaults to none. The other options are fill, sentence and fill-sentence.

### How do I make typstyle check formatting in CI?

Use the --check mode, which exits with 0 if the input is formatted correctly and non-zero if formatting is required, or --diff to see a unified diff of the changes. The README also notes that to enforce LF in CI you should use `typstyle --check --line-ending=lf` explicitly.

### What happens if typstyle cannot format part of a document?

It gives up formatting that part and prints it as is, which happens if the part contains a syntax error or contains syntaxes that are not supported by the formatter. You can also disable formatting on the next node with // @typstyle off or /* @typstyle off */.

### Is typstyle still being updated and what licence does it use?

The repository is not archived and the last push was on 2026-10-17, with the recent tags v0.15.2 from 2026-10-12, v0.15.1 from 2026-08-02 and v0.15.0 from 2026-06-20. The licence is Apache-2.0, and the crate requires rust-version 1.92 with edition 2024.

## Sources

- [License: Apache-2.0](https://github.com/typstyle-rs/typstyle/blob/master/LICENSE)
- [Project website](https://typstyle-rs.github.io/typstyle/)
- [README](https://github.com/typstyle-rs/typstyle/blob/master/README.md)
- [Releases](https://github.com/typstyle-rs/typstyle/releases)
- [typstyle-rs/typstyle on GitHub](https://github.com/typstyle-rs/typstyle)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/typstyle-rs-typstyle
