Typix
Typix aims to make it easier to use Nix in Typst projects.
- Dependency management: supports arbitrary dependencies including fonts, images, and data
- Reproducible: via a hermetically sealed build environment
Features
- Automatically fetch dependencies and compile in a single command (
nix run .#build
) - Watch input files and recompile on changes (
nix run .#watch
) - Set up a shell environment with all dependencies made available via environment variables and symlinks
- Extensible via
mkTypstDerivation
- Support for dependencies such as:
Typst packages are currently unsupported, however there is a workaround.
Getting Started
After installing Nix and enabling flakes, you can initialize a flake from the default template:
nix flake init -t github:loqusion/typix
Here are some commands you can run from the default template:
nix run .#watch
— watch the input files and recompile on changesnix run .#build
— compile and copy the output to the current directory
For more information, check out the docs.
Getting Started
First, install Nix:
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
Make sure nix-command
and flakes
are enabled:
~/.config/nix/nix.conf
experimental-features = nix-command flakes
Finally, you can initialize a flake from the default template:
nix flake init -t github:loqusion/typix
Here are some commands you can run from the default template:
nix run .#watch
— watch the input files and recompile on changesnix run .#build
— compile and copy the output to the current directory
For more info, see nix run --help
.
Adding dependencies
You can add dependencies to your flake inputs1 so that Typst compilation does not depend on the transient state of the local system: instead, any dependencies are automatically fetched and made available in a sandboxed environment.
Examples of dependencies you might want to add:
- Font files —
fontPaths
- Image files —
virtualPaths
- Data files (e.g. JSON,
TOML, XML) —
virtualPaths
For a more complete description of how to specify flake inputs, see the Nix Reference Manual section on flakerefs.
See also: https://zero-to-nix.com/concepts/flakes
nixpkgs
Many popular fonts are available as packages to nixpkgs, so if you're wanting to add a font it's good to try that before anything else.
To determine the path(s) to the files you wish to include, first run the
following command (which creates a symbolic link named result
in the current
directory):
nix-build '<nixpkgs>' --out-link result --attr PACKAGE_NAME
Then explore the result
with whichever commands you like — for instance, using
unix-tree
:
tree ./result
result
└── share
└── fonts
└── truetype
├── Roboto-BlackItalic.ttf
├── Roboto-Black.ttf
├── Roboto-BoldItalic.ttf
├── Roboto-Bold.ttf
├── RobotoCondensed-BoldItalic.ttf
├── RobotoCondensed-Bold.ttf
├── RobotoCondensed-Italic.ttf
├── RobotoCondensed-LightItalic.ttf
├── RobotoCondensed-Light.ttf
├── RobotoCondensed-MediumItalic.ttf
├── RobotoCondensed-Medium.ttf
├── RobotoCondensed-Regular.ttf
├── Roboto-Italic.ttf
├── Roboto-LightItalic.ttf
├── Roboto-Light.ttf
├── Roboto-MediumItalic.ttf
├── Roboto-Medium.ttf
├── Roboto-Regular.ttf
├── Roboto-ThinItalic.ttf
└── Roboto-Thin.ttf
Here, we can see that the relative path should be share/fonts/truetype
, so in
flake.nix
we use that information like so:
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.default = typix.lib.${system}.mkTypstDerivation {
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
};
};
}
GitHub
GitHub hosts a great deal of fonts and icon libraries, and Nix makes it easy to add GitHub repositories as flake inputs with URL-like syntax.
Here's an example of specifying a GitHub URL as a flake input and adding it to
virtualPaths
, specifying
that we want the svgs/regular
directory to be accessible from icons
:
{
inputs = {
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = { typix, font-awesome }: let
system = "x86_64-linux";
in {
packages.${system}.default = typix.lib.${system}.mkTypstDerivation {
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
With this, we can use it in Typst as if it were any other local path:
#image("icons/heart.svg")
Using local files
If all else fails, you can always manually download what you need and move it to your Typst project directory.
Here's what you need to know:
- The Typst compiler invoked by
buildTypstProject
,buildTypstProjectLocal
, etc. won't see the files you've added unless they're present in one of the source tree parameters — in practice, these aresrc
,fontPaths
, andvirtualPaths
. (This doesn't apply towatchTypstProject
.) - Paths to font files must still be passed in
fontPaths
or otherwise made known to the Typst compiler (e.g. via--font-path
).
See "Specifying sources" for information on how to expand a source tree to include the files you need.
Specifying sources
A number of derivations in Typix accept source trees as parameters, such as
src
,
fontPaths
, and
virtualPaths
. Specifying
these is usually as simple as
cleanTypstSource
in the case of
src
and string interpolation (via ${...}
) in the case of fontPaths
and
virtualPaths
, but there are situations where more is required or desirable.
Expanding a source tree
TL;DR: you can use
lib.sources.cleanSource
, but the problem with this approach is that every change to a file tracked by git will invalidate the cache and trigger a rebuild.
To include more local files1 in a source tree, you can use a
combination of different functions in lib.fileset
such as lib.fileset.unions
,
lib.fileset.fromSource
, and
lib.fileset.toSource
, like so:
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) lib;
typixLib = typix.lib.${system};
myTypstSource = typixLib.cleanTypstSource ./.;
in {
packages.${system}.default = typixLib.mkTypstDerivation {
src = lib.fileset.toSource {
root = ./.;
fileset = lib.fileset.unions [
(lib.fileset.fromSource myTypstSource)
./path.svg
./other/path.svg
./another
];
};
};
};
}
This will create a source tree that looks something like:
/nix/store/...
├── another
│ ├── path1.svg
│ ├── path2.svg
│ └── path3.svg
├── path.svg
├── other
│ └── path.svg
└── ...
lib.fileset
functions can only be used with local files, not
e.g. flake inputs, which is what
virtualPaths
is for.
Source filtering
You can do source filtering primarily using
builtins.filterSource
and functions in
lib.sources
such as
lib.sources.cleanSourceWith
.
A more detailed explanation can be found in the Nix discussion: "Filtering Source Trees with Nix and Nixpkgs".
Here's an example which picks specific files by name:
{
outputs = { nixpkgs, typix, font-awesome }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
fontAwesomeSubset = let
icons = [
"gem.svg"
"heart.svg"
"lightbulb.svg"
];
in lib.sources.cleanSourceWith {
src = "${font-awesome}/svgs/regular";
filter = path: type:
builtins.any (icon: builtins.baseNameOf path == icon) icons;
};
in {
packages.${system}.default = typix.lib.${system}.mkTypstDerivation {
virtualPaths = [
fontAwesomeSubset
];
};
};
}
Declaring a shell environment
You can automatically pull your project's dependencies into your shell by
declaring a shell environment and then activating
it with nix develop
or direnv
.
Here's an example in a flake using Typix's
devShell
:
{
outputs = { typix }: let
system = "x86_64-linux";
typixLib = typix.lib.${system};
watch-script = typixLib.watchTypstProject {/* ... */};
in {
# packages, apps, etc. omitted
devShells.${system}.default = typixLib.devShell {
fontPaths = [/* ... */];
virtualPaths = [/* ... */];
packages = [
watch-script
];
};
};
}
What this example does:
- Fonts added to
fontPaths
will be made available totypst
commands via theTYPST_FONT_PATHS
environment variable. - Files in
virtualPaths
will be recursively symlinked to the current directory (only overwriting existing files whenforceVirtualPaths
istrue
). - For convenience, the
typst-watch
script is added, which will runwatchTypstProject
.
Using Typst packages
If none of the advice on this page works for you, and there are no open issues related to your problem, feel free to open an issue.
You can find a full working example using Typst packages here.
Typst packages are still experimental, so Typix doesn't provide direct support for them yet. However, there are ways you can get them to work.
Typst packages should work out of the box for:
For the other derivation constructors, see below.
Providing the package cache
Typst caches downloaded packages for a given namespace
in {cache-dir}/typst/packages/{namespace}
.
With flake inputs defined like:
{
inputs.typst-packages = {
url = "github:typst/packages";
flake = false;
};
# Contrived example of an additional package repository
inputs.my-typst-packages = {
url = "github:loqusion/my-typst-packages";
flake = false;
};
}
...we can provide them where Typst expects them:
let
typstPackagesSrc = pkgs.symlinkJoin {
name = "typst-packages-src";
paths = [
"${inputs.typst-packages}/packages"
"${inputs.my-typst-packages}/..."
];
};
# You can use this if you only need to use official packages
# typstPackagesSrc = "${inputs.typst-packages}/packages";
typstPackagesCache = pkgs.stdenv.mkDerivation {
name = "typst-packages-cache";
src = typstPackagesSrc;
dontBuild = true;
installPhase = ''
mkdir -p "$out/typst/packages"
cp -LR --reflink=auto --no-preserve=mode -t "$out/typst/packages" "$src"/*
'';
};
in {
build-drv = typixLib.buildTypstProject {
XDG_CACHE_HOME = typstPackagesCache;
# ...
};
build-script = typixLib.buildTypstProjectLocal {
XDG_CACHE_HOME = typstPackagesCache;
# ...
};
}
Then, we can use them in a Typst file like so:
#import "@preview/example:0.1.0"
#import "@loqusion/my-package:0.2.1"
Derivations
As paraphrased from the Nix Reference Manual:
A derivation is a specification for running an executable on precisely defined input files to repeatably produce output files at uniquely determined file system paths.
The derivation constructors defined in Typix extend this behavior by running
typst compile
/typst watch
in a context where all the dependencies of your
Typst project (fonts, images, etc.) will be made available to the Typst
compiler, regardless of if they're already present on the system it runs on.
buildTypstProjectLocal
— Returns a derivation for compiling a Typst project and copying the output to the current directory.buildTypstProject
— Returns a derivation for compiling a Typst project.devShell
— Sets up a shell environment that activates withnix develop
ordirenv
.mkTypstDerivation
— A generic derivation constructor for running Typst commands.watchTypstDerivation
— Returns a derivation for a script that watches an input file and recompiles on changes.
buildTypstProject
Returns a derivation for compiling a Typst project.
If you want to build to the project directory, use
buildTypstProjectLocal
instead.
Parameters
All parameters accepted by
mkTypstDerivation
are also accepted by
buildTypstProject
. They are repeated below for convenience.
src
Source containing all local files needed in your Typst project.
fontPaths
(optional)
List of sources specifying paths to font files that will be made available to your Typst project. With this, you can compile Typst projects even when the fonts it uses are not available on your system.
Used for setting TYPST_FONT_PATHS
(see text#font
).
Example
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.default = typix.lib.${system}.buildTypstProject {
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
};
};
}
installPhaseCommand
(optional)
Command (or commands) to run during
installPhase
.
typstCompileCommand
(optional)
Base Typst command to run to compile the project. Other arguments will be appended based on the parameters you supply.
Default is typst compile
.
typstOpts
(optional)
Attrset specifying command-line options to pass to the typst
command.
These are in addition to any options you manually pass in
typstCompileCommand
.
Default:
{
format = "pdf";
}
Example
{
format = "png";
ppi = 300;
}
...will result in a command like:
typst compile --format png --ppi 300 <source> <output>
typstSource
(optional)
Typst input file to compile.
Default is main.typ
.
virtualPaths
(optional)
List of sources that will be made virtually available to your Typst project. Useful for projects which rely on remote resources, such as images or data.
Each element of the list is an attribute set with the following keys:
src
: path to source file or directorydest
(optional): path where file(s) will be made available (defaults to.
)- If
src
is a directory,dest
will be a directory containing the files in that directory.- Specifying the same
dest
for multiplesrc
directories will merge them.
- Specifying the same
- If
src
is a file,dest
will be a copy of that file.
- If
Instead of an attrset, you may use a path which will be interpreted the same as
if you had specified an attrset with just src
.
Example
You can specify dependencies in your flake input, and then use them in your project with something like:
{
inputs = {
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = { typix, font-awesome }: let
system = "x86_64-linux";
in {
packages.${system}.default = typix.lib.${system}.buildTypstProject {
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
Then, reference the files in Typst:
#image("icons/heart.svg")
Source
buildTypstProjectLocal
Returns a derivation for compiling a Typst project and copying the output to the current directory.
This is essentially a script which wraps
buildTypstProject
.
Using this derivation requires
allow-import-from-derivation
to be true
(which is the default at the time of writing).
More info: https://nixos.org/manual/nix/stable/language/import-from-derivation
Invoking the script produced by this derivation directly is currently
unsupported. Instead, use nix run
.
See this issue for more information.
Parameters
All parameters accepted by
buildTypstProject
are also accepted by
buildTypstProjectLocal
. They are repeated below for convenience.
src
Source containing all local files needed in your Typst project.
fontPaths
(optional)
List of sources specifying paths to font files that will be made available to your Typst project. With this, you can compile Typst projects even when the fonts it uses are not available on your system.
Used for setting TYPST_FONT_PATHS
(see text#font
).
Example
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
apps.${system}.default = typix.lib.${system}.buildTypstProjectLocal {
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
};
};
}
installPhaseCommand
(optional)
Command (or commands) to run during
installPhase
.
scriptName
(optional)
Name of the script that will be added to the Nix store. You can use this name after entering a development shell to invoke the script.
Default is typst-build
.
typstCompileCommand
(optional)
Base Typst command to run to compile the project. Other arguments will be appended based on the parameters you supply.
Default is typst compile
.
typstOpts
(optional)
Attrset specifying command-line options to pass to the typst
command.
These are in addition to any options you manually pass in
typstCompileCommand
.
Default:
{
format = "pdf";
}
Example
{
format = "png";
ppi = 300;
}
...will result in a command like:
typst compile --format png --ppi 300 <source> <output>
typstOutput
(optional)
Destination path for Typst output.
If omitted, it will be inferred from
typstSource
— for example,
page.typ
will become page.pdf
for PDF output.
typstSource
(optional)
Typst input file to compile.
Default is main.typ
.
virtualPaths
(optional)
List of sources that will be made virtually available to your Typst project. Useful for projects which rely on remote resources, such as images or data.
Each element of the list is an attribute set with the following keys:
src
: path to source file or directorydest
(optional): path where file(s) will be made available (defaults to.
)- If
src
is a directory,dest
will be a directory containing the files in that directory.- Specifying the same
dest
for multiplesrc
directories will merge them.
- Specifying the same
- If
src
is a file,dest
will be a copy of that file.
- If
Instead of an attrset, you may use a path which will be interpreted the same as
if you had specified an attrset with just src
.
Example
You can specify dependencies in your flake input, and then use them in your project with something like:
{
inputs = {
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = { typix, font-awesome }: let
system = "x86_64-linux";
in {
apps.${system}.default = typix.lib.${system}.buildTypstProjectLocal {
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
Then, reference the files in Typst:
#image("icons/heart.svg")
Source
devShell
Sets up a shell environment that activates with nix develop
or direnv
.
Parameters
Note: All parameters for mkShell
are also
supported.
extraShellHook (optional)
Bash statements added to the shellHook
attribute.
fontPaths
(optional)
List of sources specifying paths to font files that will be made available to your Typst project. With this, you can compile Typst projects even when the fonts it uses are not available on your system.
Used for setting TYPST_FONT_PATHS
(see text#font
).
Example
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.${system}.default = typix.lib.${system}.devShell {
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
};
};
}
forceVirtualPaths
(optional)
If there are any conflicts between virtualPaths
and files in your
project directory, they will not be overwritten unless forceVirtualPaths
is
true
.
Default is false
.
virtualPaths
(optional)
List of sources that will be made virtually available to your Typst project. Useful for projects which rely on remote resources, such as images or data.
Each element of the list is an attribute set with the following keys:
src
: path to source file or directorydest
(optional): path where file(s) will be made available (defaults to.
)- If
src
is a directory,dest
will be a directory containing the files in that directory.- Specifying the same
dest
for multiplesrc
directories will merge them.
- Specifying the same
- If
src
is a file,dest
will be a copy of that file.
- If
Instead of an attrset, you may use a path which will be interpreted the same as
if you had specified an attrset with just src
.
NOTE: Any paths specified here will not overwrite files in your project
directory, unless you set forceVirtualPaths
to true
.
Example
You can specify dependencies in your flake input, and then use them in your project with something like:
{
inputs = {
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = { typix, font-awesome }: let
system = "x86_64-linux";
in {
devShells.${system}.default = typix.lib.${system}.devShell {
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
Then, reference the files in Typst:
#image("icons/heart.svg")
Source
mkTypstDerivation
A generic derivation constructor for running Typst commands.
Parameters
Note: All parameters for stdenv.mkDerivation
1 are also available.
buildPhaseTypstCommand
Command (or commands) to run during buildPhase
. Any
output should typically be written to $out
, e.g. typst compile <source> "$out"
.
See also: Typst CLI Usage
src
Source containing all local files needed in your Typst project.
fontPaths
(optional)
List of sources specifying paths to font files that will be made available to your Typst project. With this, you can compile Typst projects even when the fonts it uses are not available on your system.
Used for setting TYPST_FONT_PATHS
(see text#font
).
Example
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.default = typix.lib.${system}.mkTypstDerivation {
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
};
};
}
installPhaseCommand
(optional)
Command (or commands) to run during
installPhase
.
virtualPaths
(optional)
List of sources that will be made virtually available to your Typst project. Useful for projects which rely on remote resources, such as images or data.
Each element of the list is an attribute set with the following keys:
src
: path to source file or directorydest
(optional): path where file(s) will be made available (defaults to.
)- If
src
is a directory,dest
will be a directory containing the files in that directory.- Specifying the same
dest
for multiplesrc
directories will merge them.
- Specifying the same
- If
src
is a file,dest
will be a copy of that file.
- If
Instead of an attrset, you may use a path which will be interpreted the same as
if you had specified an attrset with just src
.
Example
You can specify dependencies in your flake input, and then use them in your project with something like:
{
inputs = {
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = { typix, font-awesome }: let
system = "x86_64-linux";
in {
packages.${system}.default = typix.lib.${system}.mkTypstDerivation {
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
Then, reference the files in Typst:
#image("icons/heart.svg")
Source
Footnotes
stdenv
(not for the faint of heart)
watchTypstProject
Returns a derivation for a script that watches an input file and recompiles on changes.
Parameters
Note: All parameters for
writeShellApplication
are also supported
(besides text
).
fontPaths
(optional)
List of sources specifying paths to font files that will be made available to your Typst project. With this, you can compile Typst projects even when the fonts it uses are not available on your system.
Used for setting TYPST_FONT_PATHS
(see text#font
).
Example
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
apps.${system}.default = typix.lib.${system}.watchTypstProject {
fontPaths = [
"${pkgs.roboto}/share/fonts/truetype"
];
};
};
}
forceVirtualPaths
(optional)
If there are any conflicts between virtualPaths
and files in your
project directory, they will not be overwritten unless forceVirtualPaths
is
true
.
Default is false
.
scriptName
(optional)
Name of the script that will be added to the Nix store. You can use this name after entering a development shell to invoke the script.
Default is typst-watch
.
typstOpts
(optional)
Attrset specifying command-line options to pass to the typst
command.
These are in addition to any options you manually pass in
typstWatchCommand
.
Default:
{
format = "pdf";
}
Example
{
format = "png";
ppi = 300;
}
...will result in a command like:
typst watch --format png --ppi 300 <source> <output>
typstOutput
(optional)
Destination path for Typst output.
If omitted, it will be inferred from
typstSource
— for example,
page.typ
will become page.pdf
for PDF output.
typstSource
(optional)
Typst input file to compile.
Default is main.typ
.
typstWatchCommand
(optional)
Base Typst command to run to watch the project. Other arguments will be appended based on the other parameters you supply.
Default is typst watch
.
virtualPaths
(optional)
List of sources that will be made virtually available to your Typst project. Useful for projects which rely on remote resources, such as images or data.
Each element of the list is an attribute set with the following keys:
src
: path to source file or directorydest
(optional): path where file(s) will be made available (defaults to.
)- If
src
is a directory,dest
will be a directory containing the files in that directory.- Specifying the same
dest
for multiplesrc
directories will merge them.
- Specifying the same
- If
src
is a file,dest
will be a copy of that file.
- If
Instead of an attrset, you may use a path which will be interpreted the same as
if you had specified an attrset with just src
.
NOTE: Any paths specified here will not overwrite files in your project
directory, unless you set forceVirtualPaths
to true
.
Example
You can specify dependencies in your flake input, and then use them in your project with something like:
{
inputs = {
font-awesome = {
url = "github:FortAwesome/Font-Awesome";
flake = false;
};
};
outputs = { typix, font-awesome }: let
system = "x86_64-linux";
in {
apps.${system}.default = typix.lib.${system}.watchTypstProject {
virtualPaths = [
{
dest = "icons";
src = "${font-awesome}/svgs/regular";
}
];
};
};
}
Then, reference the files in Typst:
#image("icons/heart.svg")
Source
Utilities
cleanTypstSource
Filters a source tree to only contain *.typ
files and special files such as
typst.toml
.
Example
{
outputs = { nixpkgs, typix }: let
system = "x86_64-linux";
typixLib = typix.lib.${system};
in {
packages.${system}.default = typixLib.mkTypstDerivation {
src = typixLib.cleanTypstSource ./.;
};
};
}