Open-source project
chomosuke/typst-preview.nvim avatar
chomosuke/typst-preview.nvim

typst-preview.nvim renders Typst in a browser through tinymist, not through a PDF reader

Low latency typst preview for Neovim

1,018 stars62 forksLuaGPL-3.0

At a glance

What is it?
typst-preview.nvim is a Neovim plugin that previews Typst documents by running the tinymist server and streaming the result to a browser tab, which is what makes the preview feel instant and what enables clicking a position in the preview to jump to the source. It is GPL licensed, needs curl to fetch its binaries, and it is a wrapper around other people's projects rather than a renderer of its own.
Who is it for?
Choose typst-preview.nvim if you write Typst in Neovim, want a preview that updates as you type, and need to click into the preview to find the source line. Do not choose it if you need a PDF based workflow with a native reader, since the whole latency story depends on the browser based preview.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 101 days ago.
What is it written in?
Mainly Lua, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The preview is a web page, and that is the whole latency story

typst-preview.nvim calls itself a low latency Typst preview for Neovim, and the mechanism behind that claim is architectural rather than an optimisation. The plugin is a wrapper around Myriad-Dreamin/tinymist, the Typst language server, and the preview is served to a browser rather than rendered into a PDF reader window embedded in the editor.

That choice has two consequences. The first is speed: the README attributes the low latency to an incremental rendering technique that makes the preview latency as low as possible, and a browser tab receiving an updated page is not waiting for a PDF to be regenerated and reopened. The second is interactivity, because the browser side is live, so clicking in the preview can jump to the corresponding code location, and the preview can follow your cursor in Neovim.

The comparison section in the README is the clearest statement of the trade-off. It notes that niuiic/typst-preview.nvim uses typst-lsp and therefore has similar advantages and disadvantages, and specifically that it has higher latency due to the PDF reader having a delay, and does not support cross jump between code and preview. That is the difference in one sentence: a PDF reader is in the loop, and this plugin is not.

Everything the plugin needs is downloaded on first run

The dependency story is unusual for a Neovim plugin and worth reading before installing. The listed dependency is curl, and nothing else is assumed about the system. The plugin downloads the binaries it depends on itself, and the command that does it is :TypstPreviewUpdate, or require 'typst-preview'.update() from Lua. The binaries land under the Neovim data directory, at vim.fn.fnamemodify(vim.fn.stdpath 'data' .. '/typst-preview/', ':p').

The ordering constraint matters. The README states that this update command must be run before any other command can be run, and that it is implicitly run when calling setup {}. So the first setup call does a network download, and the preview cannot start until that has happened. On a machine without outbound network access, or behind a proxy, the plugin does not work until you point it at binaries you already have, which is what the dependencies_bin option is for.

The option table lists the two binaries the plugin expects, tinymist and websocat, both defaulting to nil, and setting a path there skips the download by the plugin. There is a documented warning next to that option: your version might be older than the one required. That is the whole risk of the pre-download route, and it is a mild one for tinymist, since an older server may not understand a newer Typst syntax feature.

Installation is three plugin managers and one version pin

The README gives the three common ways to install it, and they are worth comparing because the version pin differs between them. For lazy.nvim the spec is a plain table with lazy set to false, or ft set to typst, and a version of '1.*'. The comment in the spec notes that lazy.nvim implicitly calls setup {}.

lua
{
  'chomosuke/typst-preview.nvim',
  lazy = false, -- or ft = 'typst'
  version = '1.*',
  opts = {}, -- lazy.nvim will implicitly calls `setup {}`
}

For Packer.nvim the same repository is declared with use and the tag set to 'v1.*', with a config function that calls require 'typst-preview'.setup {}. For vim-plug it is a single Plug line with a tag option of 'v1.*'.

vim
Plug 'chomosuke/typst-preview.nvim', {'tag': 'v1.*'}

The version pin has a purpose beyond stability. The README explains that you can pin Typst's minor version by pinning the minor version of this plugin, giving v1.1.* as the example instead of v1.*. Matching the plugin minor to the Typst minor is how you keep the plugin on a specific Typst release. The recent tags are v1.5.0 from 2026-07-14, v1.4.2 from 2026-03-18 and v1.4.1 from 2025-12-26.

The commands are a small state machine over one preview

The command set is short and worth reading in full, because each one answers a question you would otherwise answer with a keymap. :TypstPreview starts the preview, and it takes an optional mode argument: :TypstPreview document is the default and :TypstPreview slide is for slide mode. :TypstPreviewStop stops it, and :TypstPreviewToggle toggles it.

Cursor following is controlled by three commands and one function. :TypstPreviewFollowCursor, or require 'typst-preview'.set_follow_cursor(true), scrolls the preview as the cursor moves, and it is on by default. :TypstPreviewNoFollowCursor turns it off, and :TypstPreviewFollowCursorToggle flips it using not init.get_follow_cursor(). The fourth command, :TypstPreviewSyncCursor, or require 'typst-preview'.sync_with_cursor(), scrolls the preview to the current cursor position on demand.

The combination is the interesting part. The README notes that :TypstPreviewSyncCursor can be used together with :TypstPreviewNoFollowCursor so the preview only scrolls to the current cursor position when you want it to. That is the setting for a large document where following every cursor movement is disorienting, and it is a small thing that only exists because the plugin separates the two behaviours instead of offering a single follow toggle.

Partial rendering is the option that decides how big your documents can be

The default configuration block in the README is long, and most of it is options you will not touch. One of them decides how the plugin behaves on a large document: partial_rendering is true by default, and the comment says it only renders the visible portion of the document, that it improves performance significantly for large documents, and that you should disable it if you experience rendering issues.

That is a real trade-off written into the defaults rather than hidden. Rendering only what is visible is what makes the preview cheap to update, but it means the preview is a function of the scroll position, and any feature that depends on the whole document being laid out may behave differently than you expect.

The rest of the defaults are about where the preview lives. The port is 0, which the comment describes as a random port, and host is 127.0.0.1, with an explicit note that '0.0.0.0' is not supported and links to the upstream tinymist issue explaining that it will not be. That binding restriction is worth reading if you were planning to preview on a remote machine over SSH. The open_cmd option is nil by default and takes a format string with %s for the output link, with the example firefox %s -P typst-preview --class typst-preview.

There is also invert_colors, defaulting to 'never', which can be set to 'always' to invert black and white, to 'auto' to invert depending on whether the browser has dark mode enabled, or to a JSON string such as '{"rest": "<option>","image": "<option>"}' to treat images separately from everything else.

The project root is found by marker, and the function is replaceable

The get_root option decides which directory counts as the Typst project, and that has a visible consequence for imports. The default implementation first checks the TYPST_ROOT environment variable and returns it if set. Otherwise it takes the directory of the main file and searches upward with vim.fs.find for a project marker, looking for typst.toml or .git, and returns the directory of the first match. If nothing is found it falls back to the main file's directory.

The comment above that code explains the intent: looking for a project marker so imports from parent directories stay inside root. In other words, without a marker, a file that imports from a directory above the main file can fail, and the marker file is what tells the plugin where the boundary is. If you work in a monorepo where a typst.toml does not exist, the get_root option is the place to override this, and the presence of a callable option rather than a fixed path is the point.

The companion option is get_main_file, which takes the path of the buffer and returns it unchanged by default, and that is the hook for projects where one file is the entry point for many others. There is also extra_args for passing extra arguments to the previewer, with the example "--input=ver=draft" and "--ignore-system-fonts", which matters for anyone reproducing a specific Typst invocation.

The last piece is the debug option, false by default, which turns on logging to vim.fn.stdpath 'data' .. '/typst-preview/log.txt'. When a preview misbehaves, that file is the first place to look.

A GPL plugin that is mostly other people's work

The licence is GPL 3, with the text in the LICENSE file at the repository root. The repository layout is small and conventional for a Neovim plugin: lua/ for the plugin code, plugin/ for the command definitions, doc/ for the Vim help file, assets/ for images, plus a .luarc.json and a .stylua.toml for the Lua language server and the formatter.

The credit section is unusually direct about the lineage. The README states that the plugin would not be possible without the work of Enter-tainer/typst-preview and Myriad-Dreamin/tinymist, and asks that those projects be starred in turn. The comparison section links to a write up by the author of Enter-tainer/typst-preview, which is where the deeper analysis of how the approaches differ lives.

So the honest description is that typst-preview.nvim is the Neovim front end for tinymist's preview server, with the web preview and cross jump design inherited from that lineage, plus the plugin specific work of binary management, cursor sync and configuration. The last push was on 2026-07-14, the same day as the v1.5.0 tag, and the project is not archived. There is also a test.typ file at the repository root and a .github directory, so the repository carries a document to check the plugin against.

Editorial conclusion

Choose typst-preview.nvim if you write Typst in Neovim, want a preview that updates as you type, and need to click into the preview to find the source line. Do not choose it if you need a PDF based workflow with a native reader, since the whole latency story depends on the browser based preview. Verify first that curl is available and that the tinymist and websocat binaries download and run on your machine, and pin the plugin to a minor version such as v1.1.* when you need to match a particular Typst release.

Frequently asked questions

How do I install typst-preview.nvim in Neovim?

With lazy.nvim, add the plugin table for chomosuke/typst-preview.nvim with version '1.*' and opts = {}. Packer.nvim and vim-plug are also documented, using the tag v1.*. The only system dependency listed is curl.

Why does typst-preview.nvim need curl?

The plugin downloads the binaries it depends on itself, into the typst-preview folder under the Neovim data directory. The :TypstPreviewUpdate command performs the download and must run before any other command, and it happens implicitly when setup is called.

How do I match the plugin version to my Typst version?

The README says you can pin Typst's minor version by pinning the minor version of the plugin, for example v1.1.* instead of v1.*.

What is the difference between typst-preview.nvim and niuiic/typst-preview.nvim?

The README states that the other plugin uses typst-lsp and therefore has higher latency because the PDF reader adds a delay, and that it does not support cross jump between code and preview. typst-preview.nvim previews through tinymist in a browser instead.

Can typst-preview.nvim preview a remote document over SSH?

The default host is 127.0.0.1, and the README notes that '0.0.0.0' is not supported and links to the upstream tinymist issue explaining why. You can set host yourself, and set open_cmd to control which browser opens the preview link.

Official sources

  1. chomosuke/typst-preview.nvim on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chomosuke-typst-preview-nvim.svg)](https://hysenlabs.com/projects/chomosuke-typst-preview-nvim)

Was this useful?