Comparing Typst packages for QR code rendering
Published
When looking for packages on Typst Universe, you can find several packages for creating QR codes. I have recently needed to generate EPC QR codes (for making SEPA bank transfers), had to look at my options, and decided I'd write up the results.
This is not the most thorough investigation one could make, but I did quickly look at performance, quality, ability to generate non-QR codes, as well as license and usage in the package ecosystem – at least as far as it was easy to tell from the above Universe searches.
The Contestants
Searching for both "QR" and "codes", the following dedicated (QR) code generation packages can be found:
Cades
cades is based qrcode-svg, a JS library running in Typst via Jogs.
License: MIT
Codetastic
codetastic was released (and last updated) in September 2023. Other than QR codes, it can generate UPC-A and a number of EAN bar codes. What makes this one a bit special is that it seems to be implemented in pure Typst instead of bundling a preexisting library for generating the codes.
License: MIT
Rustycure
rustycure is again a pure QR code generator. It is newer, released in 2025, and based on the Rust qrcode crate. It can output raw SVG bytes in addition to ready-to-consume images; that may be useful if you plan to, for some reason, process the image with something like grayness.
Unlike the other packages, rustycure wraps its QR codes with a "quiet zone" by default, i.e. a region without content to make sure there is surrounding contrast.
License: EUPL-1.2
Tiaoma
tiaoma is the package I had mostly been using so far. It is based on the Zint C library.
There are lots of customization options available through Zint, however I found that discovering them in the documentation was not super easy; one reason here is that these options are given as a single options dictionary argument, so the function signature does not directly list the options. On the other hand, I have to mention that I simply lack the familiarity with the other packages, to make similar judgments for them.
License: MIT
Zebra
zebra was only released in February 2026 and like rustycure, it is based on the qrcode Rust crate. Other than QR codes, it also supports data matrix codes via the so-named crate. Also, props to the author for having a small comparison of the packages int heir README as well.
License: MIT
Output quality
Thanks to the Zebra author for bringing this to my attention: of course, not all QR code generators automatically produce the same quality! Among the packages I was looking at, I was able to identify three options:
-
Basic: QR codes consist of squares; these are drawn separately. This can lead to edges between filled areas in the rendered PDF, especially at high zoom levels.
The cades and codetastic packages fall into this category.
-
Single path: Squares are drawn, but combined into a single path. This gives renderers a better chance at merging these, and indeed in my tests, I could not spot any edges. (Disclaimer: I was not able to fully confirm this behavior, but this was my conclusion from zebra's description and output quality.)
The rustycure package falls into this category.
-
Optimized path: Squares are combined into a more complex shape, and that shape is output as a single path. The Zebra README contains an illustrative diagram.
Both tiaoma and zebra do this.
One thing to note too is that not all these generators output the same image for the same data! I did not investigate further here, but this is probably due to different default error correction levels and/or compression strategies.
Ecosystem usage
QR codes contain data, and various packages support specific data standards. What other packages are there, and what generator do they use under the hood?
Payment/finance QR codes
Based on rustycure:
-
sepay: Generate EPC QR codes for SEPA credit transfers (MIT)
- invoice-pro: DIN 5008 compliant invoice template (MIT)
Based on tiaoma:
- payqr-swiss: generate Swiss QR bills (LGPL-3.0-only)
- olaii-upn-qr: Obrazec UPN QR (MIT)
- laskutys: invoices satisfying Finnish standards (MIT-0)
Doing its own thing:
- cekinar: Slovenian UPN QR payment slips (EUPL-1.2), uses a Zig port of another QR code generator library.
Misc QR codes
Based on tiaoma:
- materialize: Matter setup codes for smart home devices (MIT)
Performance
Now the big one: how fast are these? The implementations range from interpreted Javascript over pure Typst all the way to native Rust and C; how much difference does it make?
Each package was run three times with time (and the user time was taken); for rustycure, I actually performed four runs because the first run was a bit slower while the others were very consistent, so I think this was an outlier and it is excluded from the numbers below. Each run contained 50 QR codes containing progressively longer lorem texts in a grid. The runs actually compiled the full PDF, so there is a bit of SSD I/O in these numbers.
I don't have extensive benchmarking experience, so take the exact numbers with a grain of salt – you can find the test document and results at https://codeberg.org/ensko/blog/src/branch/main/content/blog/assets/009. However, I think the proportions speak a fairly clear language:
| Package | Mean time (s) | Standard Deviation (s) |
|---|---|---|
| tiaoma | 1.37 | 0.002 |
| rustycure | 3.41 | 0.011 |
| zebra | 3.87 | 0.001 |
| codetastic | 11.35 | 0.008 |
| cades | 238.03 | 5.651 |
Tiaoma, the C implementation, is solidly in first place. Rustycure and zebra come soon after, with zebra trading a bit of performance for better output quality, it seems. Codetastic, the pure Typst implementation, takes considerably longer, and Cades, the Javascript implementation, tested my patience!
Conclusion
The Typst QR code landscape is surprisingly diverse: C, Rust, Typst, and Javascript are all represented. Despite the new contenders, it seems that tiaoma is still at the top of the pack! I came into this looking for a payment QR code package, and ended up with the sepay package, which is based on rustycure. It could be faster if it used tiaoma, but the speed is nevertheless acceptable.
I appreciated zebra's README, which gave me valuable hints that I should cover quality in this post, and I also like that they explicitly focused on this aspect. By that README, tiaoma is still a top choice in that regard, so there is no need to compromise if you want top performance.
Finally – no shade to this early package, stresstesting Typst's Javascript capabilities – but if you use cades in your Typst documents, you can definitely do better!
Thanks for reading! And if I missed any packages in my research, let me know on the forum or on Discord.