straight.el

straight.el: next-generation, purely functional package manager for the Emacs hacker.

Please check out the FAQ and news :)

Gitter chat

Features

Note: straight.el is a replacement for package.el, not use-package. use-package can be used with either package.el or straight.el.

Guiding principles

Getting started

Note: straight.el supports a minimum version of Emacs 25.1, and works on macOS, Windows, and most flavors of Linux. You must install Git in order to use straight.el.

First, place the following bootstrap code in your init-file:

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

(If raw.githubusercontent.com is blocked by your ISP, try replacing the URL with https://radian-software.github.io/straight.el/install.el. Or you can clone straight.el manually to ~/.emacs.d/straight/repos/straight.el.)

Here are some variables you may be interested in (some of them must be set before the bootstrap code runs, if they might affect how straight.el itself is loaded):

You should remove any code that relates to package.el; for example, references to package-initialize, package-archives, and (if you’re using use-package) :ensure or use-package-always-ensure.

Users of Emacs versions >= 27 will want to add:

(setq package-enable-at-startup nil)

to their early init-file to prevent package.el loading packages prior to their init-file loading.

While it is technically possible to use both package.el and straight.el at the same time, there is no real reason to, and it might result in oddities like packages getting loaded more than once.

Debugging

Install packages

Out of the box, you can install any package listed on MELPA, GNU ELPA, or Emacsmirror, which is to say any package in existence. (Although MELPA is used as a package listing, packages are installed by cloning their Git repositories rather than by downloading tarballs like package.el does.) To install a package temporarily (until you restart Emacs), run M-x straight-use-package and select the package you want. To install a package permanently, place a call to straight-use-package in your init-file, like:

(straight-use-package 'el-patch)

Note that installing a package will activate all of its autoloads, but it will not actually require the features provided by the package. This means that you might need to use require or autoload for some antiquated packages that do not properly declare their autoloads.

To learn more, see the documentation on the package lifecycle.

But what about my fork of (obscure .el package)?

Instead of passing just a package name to straight-use-package, you can pass a list (“recipe”). You can see the default recipe for any given package by running M-x straight-get-recipe. For example, the recipe for el-patch is:

(el-patch :type git :host github :repo "radian-software/el-patch")

So, if you have forked el-patch and you want to use your fork instead of the upstream, do:

(straight-use-package
 '(el-patch :type git :host github :repo "your-name/el-patch"))

In fact, straight.el has explicit support for using a forked package, since this is so common:

(straight-use-package
 '(el-patch :type git :host github :repo "radian-software/el-patch"
            :fork (:host github
                   :repo "your-name/el-patch")))

In the above, :type git may be omitted if you leave straight-default-vc at its default value of git. Parts of the :fork keyword may be omitted as well. One common case is when your fork is on the same host and has the same name as the upstream repository. In this case, assuming straight-host-usernames is set, specifying a fork is as simple as:

(straight-use-package
 '(el-patch :type git :host github :repo "radian-software/el-patch"
            :fork t))

Note that straight.el doesn’t do any Git operations during startup unless it needs to clone a package from scratch. This is for performance. You can explicitly request for straight.el to fix up the Git configuration after you change a package recipe, e.g. to add a fork. See Automatic repository management below.

To learn more, see the documentation on the recipe format and the Git backend.

Integration with use-package

use-package is a macro that provides convenient syntactic sugar for many common tasks related to installing and configuring Emacs packages. Of course, it does not actually install the packages, but instead defers to a package manager, like straight.el (which comes with use-package integration by default).

To use use-package, first install it with straight.el:

(straight-use-package 'use-package)

Now use-package will use straight.el to automatically install missing packages if you provide :straight t:

(use-package el-patch
  :straight t)

You can still provide a custom recipe for the package:

(use-package el-patch
  :straight (el-patch :type git :host github :repo "radian-software/el-patch"
                      :fork (:host github
                             :repo "your-name/el-patch")))

The :straight keyword accepts backquoted forms. This makes it possible to dynamically compute part of the recipe:

(use-package el-patch
  :straight `(el-patch :type git
                       :repo ,(alist-get 'el-patch my-package-urls)))

Specifying :straight t is unnecessary if you set straight-use-package-by-default to a non-nil value. (Note that the variable use-package-always-ensure is associated with package.el, and you should not use it with straight.el.)

To learn more, see the documentation on straight.el’s use-package integration.

Edit packages locally

One of the biggest strengths of straight.el is that editing packages locally is trivial. You literally just edit the files (find-function and friends all work as you would expect). Packages will be automatically rebuilt if necessary when Emacs next starts up.

You can even commit your changes and push or pull to various remotes using Git. You have complete control over your packages’ Git repositories.

To learn more, see the documentation on the package lifecycle.

Automatic repository management

While being able to make arbitrary changes to your packages is very powerful, it can also get tiring to keep track of the all those changes. For this reason, straight.el provides a suite of powerful interactive workflows to perform bulk operations on your packages.

All of these commands are highly interactive and ask you before making any changes. At any point, you can stop and perform manual operations with Magit or other tools in a recursive edit.

To learn more, see the documentation on bulk repository management.

Configuration reproducibility

To save the currently checked out revisions of all of your packages, run M-x straight-freeze-versions. The resulting file (~/.emacs.d/straight/versions/default.el), together with your init-file, perfectly define your package configuration. Keep your version lockfile checked into version control; when you install your Emacs configuration on another machine, the versions of packages specified in your lockfile will automatically be checked out after the packages are installed. You can manually revert all packages to the revisions specified in the lockfile by running M-x straight-thaw-versions.

To learn more, see the documentation on version lockfiles.

Conceptual overview

This section describes, at a high level, how the different mechanisms in straight.el play together. This illustrates how straight.el manages to accomplish all of its guiding principles.

TL;DR

straight.el operates by cloning Git repositories and then symlinking files into Emacs’ load path. The collection of symlinked files constitutes the package, which is defined by its recipe. The recipe also describes which local repository to link the files from, and how to clone that repository, if it is absent.

When you call straight-use-package, the recipe you provide is registered with straight.el for future reference. Then the package’s repository is cloned if it is absent, the package is rebuilt if its files have changed since the last build (as determined by find(1)), and its autoloads are evaluated.

You can also provide only a package name, in which case the recipe will be looked up in one of several configurable recipe repositories, which are just packages themselves (albeit with the build step disabled).

straight.el determines which packages are installed solely by how and when straight-use-package is invoked in your init-file.

What is a package?

A package is a collection of Emacs Lisp (and possibly other) files. The most common case is just a single .el file, but some packages have many .el files, and some even have a directory structure.

Note that a package is defined only as a collection of files. It doesn’t necessarily correspond to a Git repository, or an entry on MELPA, or anything like that. Frequently there is a relationship between all of these concepts, but that relationship does not always have to be direct or one-to-one.

A package also has a name, which must be unique. This is the name that is used for the folder holding the package’s files. It is frequently the same as the name of a Git repository, or an entry on MELPA, but again this does not have to be the case.

Where do packages come from?

If you really wanted all of your packages to be unambiguously defined, you could just copy and paste all of their files into version control. But that would defeat the purpose of using a package manager like straight.el. In straight.el, packages are defined by two sources of information:

The local repository is just a directory containing some files. Of course, it also has a name, which may or may not be the same as the package’s name. Frequently, the local repository is also a Git repository, but this is not necessary.

The build recipe is not a literal data structure. It is a concept that represents a certain subset of the package’s recipe. Specifically, the :files, :local-repo, and :build keywords.

To transform this information into an actual package that Emacs can load, straight.el builds the package. This means that some symbolic links are created in the package’s directory that point back into the local repository’s directory. Exactly how these symlinks are created is determined by the :files directive, and which local repository the symlinks point to is determined by the :local-repo directive.

After the symlinks are created, the resulting files are byte-compiled, and their autoloads are generated and written into a file in the package’s directory.

If :build nil is specified, however, this entire process is skipped. This mechanism is used for recipe repositories.

What does this look like on disk?

The local repositories are kept in ~/.emacs.d/straight/repos, and the built packages are kept in ~/.emacs.d/straight/build. If you have initialized straight.el and loaded package el-patch, then your ~/.emacs.d/straight directory will look roughly like this (some irrelevant details have been omitted for pedagogical purposes):

straight
├── build
│   ├── el-patch
│   │   ├── el-patch-autoloads.el
│   │   ├── el-patch.el -> ~/.emacs.d/straight/repos/el-patch/el-patch.el
│   │   └── el-patch.elc
│   └── straight
│       ├── straight-autoloads.el
│       ├── straight.el -> ~/.emacs.d/straight/repos/straight.el/straight.el
│       └── straight.elc
└── repos
    ├── el-patch
    │   ├── CHANGELOG.md
    │   ├── LICENSE.md
    │   ├── README.md
    │   └── el-patch.el
    └── straight.el
        ├── LICENSE.md
        ├── Makefile
        ├── README.md
        ├── bootstrap.el
        ├── install.el
        └── straight.el

As you can see, the package names are el-patch and straight. While el-patch is built from a local repository of the same name, straight is built from a local repository by the name straight.el. Also note that only .el files are symlinked, since only they are relevant to Emacs.

Where do repositories come from?

Local repositories provide a way to define packages without specifying the contents of all of their files explicitly. But that’s not helpful without a higher-level way to define local repositories without specifying the contents of all of their files. In straight.el, local repositories are defined by two sources of information:

The fetch recipe is, like the build recipe, a concept representing a certain subset of the package’s overall recipe. The situation is more interesting here because straight.el supports multiple version-control backends. The version-control backend specified by the fetch recipe is determined by the :type directive (which defaults to straight-default-vc). Each version-control backend then accepts some set of additional directives. For example, the git backend accepts:

If a local repository is not present, then its fetch recipe describes how to obtain it. This is done using the straight-vc-clone function, which delegates to one of the backend implementations of the clone operation, according to :type. (The option :type built-in is a special case that results in all version-control operations for the package being ignored. You can also use :type nil to accomplish the same, but with the difference that the package is still loaded from its specified :local-repo.)

However, even with a particular repository source specified, there is still the question of which version of the repository to use. This is where the version lockfiles come in. When a local repository is cloned, the version lockfiles are searched to see if there is a particular commit specified for that local repository’s name. If so, that commit is checked out. (For the git backend, commits are 40-character strings representing SHA-1 hashes, but the representation of a commit identifier could be different across different backends.)

The straight-freeze-versions and straight-thaw-versions methods also use backend-delegating methods; in this case, they are straight-vc-get-commit and straight-vc-check-out-commit.

The fetch recipe and version lockfiles, together with the configuration options for straight.el, precisely define the state of a local repository. Of course, you may make any changes you want to the local repository. But this information defines a “canonical” state that you may revert to at any time.

When this information is combined with the build recipe, straight.el is able to construct canonical, universal versions of your Emacs packages that will be the same everywhere and forever.

Note that you do not have to provide fetch recipes or version lockfiles. You may manage your local repositories manually, if you wish, although this has obvious disadvantages in terms of repeatability and maintainability.

What does it mean to load a package?

A prerequisite to loading a package is making sure the package has been built. After that is done, loading the package means adding its directory to the load path and evaluating its autoloads file.

Adding the directory to the load path means that you can use require to load the package’s files. Note that straight.el does not do this for you, since loading packages immediately is usually not necessary and it immensely slows down Emacs startup.

Evaluating the autoloads file means that calling the functions that are defined in the autoloads file will automatically require the files that define those functions. All modern packages define their functions in autoloads and are designed to be loaded on-demand when those functions are called. Antiquated packages may need you to explicitly define autoloads, or to just require the package right away.

Where do recipes come from?

straight-use-package does not require an actual recipe. You can just give it a package name, and it will look up the recipe. This is done using recipe repositories. Recipe repositories are set up as a swappable backend system, much like the version-control backend system.

A recipe repository consists of four parts:

Note that recipe repositories are implemented as regular packages! This means that all the usual package management operations work on them as well. It also means that you use straight-use-package to register them (although typically you will provide arguments to straight-use-package so that the recipe repository is only registered, and not cloned until it is needed; see the section on straight-use-package).

If you give straight-use-package just a package name, then each recipe repository in straight-recipe-repositories is checked for a recipe for that package. Once one is found, it is used. Otherwise, an error is signaled (unless the package is built-in to Emacs, according to package.el).

Note that straight.el uses its own recipe format which is similar, but not identical, to the one used by MELPA (see the section on the recipe format for information on the differences). The recipe repository backends abstract over the formatting differences in different recipe sources to translate recipes into the uniform format used by straight.el. When you run M-x straight-get-recipe, the translated recipe is what is returned.

What happens when I call straight-use-package?

There are three actions that straight-use-package can take:

These actions must be performed in order. Depending on the arguments you pass to straight-use-package, one, two, or all three may be performed.

The normal case is to do all three. The fetch recipe is only required if the local repository is actually missing, but the build recipe is always required.

Deferred installation can be accomplished by telling straight-use-package to stop if the local repository is not already available. The deferred installation can be triggered by invoking straight-use-package again, but telling it to go ahead and clone the repository (this is the default behavior). Because straight-use-package already registered the package’s recipe the first time, you don’t have to provide it again.

In some extraordinary circumstances (such as when straight.el is bootstrapping its own installation), it may be desirable to clone a package’s local repository if it is missing, but to stop before building and loading the package. This can also be done by straight-use-package.

What does it mean to register a package?

Package registration is the first action taken by straight-use-package, before building and cloning. First, if only a package name was provided to straight-use-package, a recipe is obtained from the configured recipe repositories. Next, the resulting recipe is recorded in various caches.

This is important, since it allows for several things to happen:

How does straight.el know when to rebuild packages?

When you request for straight.el to load a package (using straight-use-package), it first checks if the package needs to be rebuilt. This means that some of the files in its local repository have been modified since the last time the package was built. straight.el uses an optimized find(1) command to check for package modifications, and it uses some caching mechanisms to perform bulk find(1) operations on multiple packages, to speed up these checks (although it never performs optimizations that may result in erroneous behavior).

This check occurs during Emacs init, when your init-file makes calls to straight-use-package. You may notice a significant delay on the first straight-use-package call, because this is when straight.el performs a bulk find(1) call and caches the results for later usage (this speeds up init considerably). The total delay is likely to be on the order of 100ms for a double-digit number of packages.

The rebuild detection system is what allows for you to make changes to packages whenever you would like, without performing any additional operations.

(Packages are also rebuilt when their recipes change, of course.)

How does straight.el know what packages are installed?

straight.el does not require you to declare a central list of packages anywhere, like Cask does. Instead, it determines what packages are to be loaded implicitly, by your invocations of straight-use-package during Emacs initialization. Furthermore, straight.el allows you to install packages after initialization using M-x straight-use-package (or even by evaluating straight-use-package forms). However, straight.el still provides advanced features such as bulk package management and version locking. This creates some interesting challenges which other package managers do not have to deal with.

straight.el solves these problems using a concept called transactions. The operation of the transaction system is mostly transparent to the user, at least in recent versions of straight.el. Basically, it provides a way for straight.el to keep track of what happens within a single user operation (e.g. evaluate a buffer of straight-use-package calls, or load the init-file).

straight.el uses the transaction system to keep track of what packages you request in your init-file. If you invoke straight-use-package interactively, then this invalidates that information, since you have now requested a package that is not in your init-file. For this reason, if you have invoked straight-use-package interactively, running M-x straight-freeze-versions will prompt you to first reload your init-file.

Note: reloading your init-file must have the effect of running all of the same straight.el-related functions again. For example, if you bootstrap straight.el in a sub-file that you only require instead of load, then the reloading functionality will not work correctly and you may receive the message Caches are still outdated; something is seriously wrong. See #437 for discussion.

Comparison to other package managers

(Disclaimer: while I try to be as objective and comprehensive as possible here, I’m obviously biased. Please submit corrections if I have unfairly disparaged your favorite package manager!)

There are many package managers for Emacs, ranging from simple scripts to download files from EmacsWiki to full-featured package management solutions like straight.el. Here are the most feature-rich alternatives to straight.el:

TL;DR

Here is a summary of the main differences in philosophy between the package managers:

And here is a brief list of the main reasons you might not want to use straight.el:

Comparison to package.el

Advantages of straight.el

Advantages of package.el

Additional notes

Comparison to Quelpa

Advantages of straight.el

Advantages of Quelpa

Additional notes

Comparison to Cask

I have not used Cask extensively, so please feel especially free to offer corrections for this section.

Advantages of straight.el

Advantages of Cask

Comparison to el-get

I have not used el-get extensively, so please feel especially free to offer corrections for this section.

Advantages of straight.el

Advantages of el-get

Comparison to Borg

Advantages of straight.el

Advantages of Borg

Comparison to the manual approach

Advantages of straight.el

Advantages of the manual approach

User manual

This section tells you everything you need to know about the user-facing features of straight.el. For implementation details, see the developer manual. It may also be helpful to get some perspective on the overarching concepts of straight.el from the conceptual overview.

Bootstrapping straight.el

In order to use straight.el, you will need to somehow get it loaded into Emacs. (This is easy for package.el, since package.el is built in to Emacs. straight.el must work a little harder.)

straight.el comes with a file to do just this, bootstrap.el. All you need to do is load that file. You can do this with M-x load-file or by a call to load in your init-file. However, there is an obvious shortcoming: bootstrap.el will only be available once straight.el is already installed.

You could just invoke git clone from your init-file, if straight.el is not installed, but then you would have to manually take care of selecting the correct branch, parsing your version lockfile to check out the right revision, and so on. Instead, you can just use this snippet, which uses a copious amount of magic to take care of all these details for you:

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

Despite the reference to develop, this snippet actually installs from the master branch by default, just like every other package. Furthermore, the correct revision of straight.el is checked out, if you have one specified in your lockfile. Even better, you can override the recipe for straight.el, just like for any other package.

Installing packages programmatically

The primary entry point to straight.el is the straight-use-package function. It can be invoked interactively (for installing a package temporarily) or programmatically (for installing a package permanently). This section covers the programmatic usage; see later for interactive usage.

Here is the basic usage of straight-use-package:

(straight-use-package 'el-patch)

This will ensure that the package el-patch is installed and loaded. (Note that straight-use-package takes a symbol, not a string, for the name of the package.) Precisely, this is what happens:

Package authors should note that straight.el checks for dependencies that are specified in the package.el format. To spare you reading that documentation, this is either a Package-Requires header in PACKAGENAME.el, or an argument to a define-package invocation in PACKAGENAME-pkg.el. Despite the many shortcomings of package.el, it has done a good job of creating a standardized format for dependency declarations.

There is one exception to the above statement: not all entries specified in the Package-Requires header necessarily correspond to packages. For example, specifying a minimum Emacs version for a package is done by depending on the emacs pseudo-package. Such packages are simply ignored by straight.el, using the variable straight-built-in-pseudo-packages.

Note that loading a package does not entail invoking require on any of its features. If you wish to actually load the files of the package, you need to do this separately. This is because most packages do not need to be loaded immediately, and are better served by the autoload system.

Installing with a custom recipe

straight-use-package can also take a list instead of a symbol. In that case, the first member of the list is a symbol giving the package name, and the remainder of the list is a property list providing information about how to install and build the package. Here is an example:

(straight-use-package
 '(el-patch :type git :host github :repo "radian-software/el-patch"
            :fork (:host github
                   :repo "your-name/el-patch")))

If you give straight-use-package just a package name, then a recipe will be looked up by default (see the section on recipe lookup). You can see the default recipe for a package by invoking M-x straight-get-recipe.

If straight-allow-recipe-inheritance is non-nil, then you only need to specify the components of the recipe that you want to override. All other components will still be looked up in the default recipe. In the example above, we are only interested in changing the :fork component. Therefore if straight-allow-recipe-inheritance is set, the recipe could be simplifed as follows:

(straight-use-package
 '(el-patch :fork (:repo "your-name/el-patch")))

or even simpler:

(straight-use-package
 '(el-patch :fork "your-name/el-patch"))

The :files keyword and all version control keywords support inheritance.

To learn more, see the section on the recipe format.

Additional arguments to straight-use-package

The full user-facing signature of straight-use-package is:

(straight-use-package PACKAGE-OR-RECIPE &optional NO-CLONE NO-BUILD)

As discussed previously, by default straight-use-package will do three things:

By providing the optional arguments, you may cause processing to halt before all three of these tasks are completed. Specifically, providing NO-CLONE causes processing to halt after registration but before cloning, and providing NO-BUILD causes processing to halt after cloning (if necessary) but before building and loading.

straight.el supports lazy-loading by means of a special value for NO-CLONE, the symbol lazy. If this symbol is passed, then processing will halt at the clone step, unless the package is already cloned. This means that the package is built and loaded if it is already installed, but otherwise installation is deferred until later. When you want to trigger the lazy installation, simply call straight-use-package again, but without NO-CLONE. (There is no need to pass the recipe again; see recipe lookup.)

You can also pass functions for NO-CLONE or NO-BUILD, which will be called with the package name as a string; their return values will then be used instead.

Note that if it makes no sense to build a package, then you should put :build nil in its recipe, rather than specifying NO-BUILD every time you register it with straight.el. (This is especially relevant when writing recipes for recipe repositories.)

Variants of straight-use-package

For convenience, straight.el provides some functions that wrap straight-use-package with particular arguments, to cover all of the common cases. Each of these functions takes only a package name or recipe, and no additional arguments.

Customizing when packages are built

By default, when straight.el is bootstrapped during Emacs init, it uses a bulk find(1) command to identify files that were changed since the last time a package depending on them was built. These packages are then rebuilt when they are requested via straight-use-package. Normally, straight.el will try to detect what sort of find(1) program is installed, and issue the appropriate command. If it makes a mistake, then you can manually customize straight-find-flavor. Alternately, you can install GNU find and customize the variable straight-find-executable to point to it.

For about 100 packages on an SSD, calling find(1) to detect modifications takes about 500ms. You can save this time by customizing straight-check-for-modifications. This is a list of symbols which determines how straight.el detects package modifications. The default value is (find-at-startup find-when-checking), which means that find(1) is used to detect modifications at startup, and also when you invoke M-x straight-check-package or M-x straight-check-all. If you prefer to avoid this performance hit, or do not have find(1) installed, then you can remove these symbols from the list. In that case, you will probably want to add either check-on-save or watch-files to the list.

check-on-save causes straight.el to use before-save-hook to detect package modifications as you make them (modifications made by the straight.el repository management commands are also detected). This reduces init time, but modifications made outside of Emacs (or modifications that bypass before-save-hook) are not detected. Pull requests extending the number of cases in which straight.el is able to detect live modifications are welcome. Also, for the sake of efficiency, this form of modification checking is restricted to subdirectories of ~/.emacs.d/straight/repos, so you must put your local repositories into that directory for it to work. (Pull requests to change this would be welcome.)

watch-files causes straight.el to automatically invoke a filesystem watcher to detect modifications as they are made, inside or outside of Emacs. For this setting to work, you must have python3 and watchexec installed on your PATH. By default, the watcher persists after Emacs is closed. You can stop it manually by running M-x straight-watcher-stop, and start it again by running M-x straight-watcher-start. The watcher script is designed so that when one instance is started, all the others gracefully shut down, so you don’t have to worry about accidentally ending up with more than one. There is nothing exciting in the process buffer for the watcher, but if you are interested in it then its name is given by straight-watcher-process-buffer. (By default, the name has a leading space so that the buffer does not appear in the buffer list.)

There is probably no good reason to use both check-on-save and watch-files at the same time. Your configuration can dynamically switch between which one is used depending on (executable-find "watchexec") or similar.

If you prefer to eschew automatic package rebuilding entirely, you can just set straight-check-for-modifications to nil. In that case, packages will only be rebuilt when metadata (e.g. the recipe or the Emacs version) changes, or when you manually invoke M-x straight-rebuild-package or M-x straight-rebuild-all.

Regardless of your preferred setting for straight-check-for-modifications, you should set it before the straight.el bootstrap snippet is run, since hooks relating to this variable are set during bootstrap.

On Microsoft Windows, find(1) is generally not available, so the default value of straight-check-for-modifications is instead (check-on-save).

Custom or manual modification detection

You can also use the low-level functions for modification detection directly.

The function straight-register-repo-modification takes a string (e.g. "straight.el") corresponding to the name of a local repository, and marks all packages from that local repository to be rebuilt at next Emacs startup. This function silently ignores local repositories which contain slashes, a limitation which might be removed in future.

The function straight-register-file-modification takes no arguments and checks if the file visited by the current buffer (if any) is contained by any local repository. If so, it delegates to straight-register-repo-modification. The check-on-save value for straight-check-for-modifications just adds straight-register-file-modification to before-save-hook.

Summary of options for package modification detection
find-at-startup

Save build timestamps and run find(1) at startup to detect changes

check-on-save

Use before-save-hook to detect changes

watch-files

Run filesystem watcher to detect changes


Customizing how packages are built

By specifying :build nil in a package’s recipe, you may prevent the package from being built at all. This is usually useful for recipe repositories which do not bundle executable Lisp code. (Make sure to use straight-use-recipes for registering recipe repositories.)

Autoload generation

By specifying :build (:not autoloads) in a package’s recipe, you may prevent any autoloads provided by the package from being generated and loaded into Emacs. This is mostly useful if the package provides a large number of autoloads, you know you need only a few of them, and you wish to optimize your startup time (although this is almost certainly premature optimization unless you really know what you’re doing). You can also customize the variable straight-disable-autoloads to effect this change on all recipes which do not explicitly disable autoloads via the :build keyword.

Byte compilation

By specifying :build (:not compile) in a package’s recipe, you may inhibit byte-compilation. See this issue for discussion of why this might be useful. You can also customize the variable straight-disable-compile to effect this change on all recipes which do not explicitly disable byte-compilation via the :build keyword.

Native compilation

Experimental support for native compilation of Emacs Lisp code can be enabled in the latest master branch of the official Emacs repository (see gccemacs). When running on this version of Emacs, straight.el will perform native compilation of packages.

By specifying a :build (:not native-compile) in a package’s recipe, you may inhibit native compilation. You can also customize the variable straight-disable-native-compile to effect this change on all recipes which do not explicitly disable native-compilation via the :build keyword.

Native compilation requires byte-compilation, so :build (:not compile) and straight-disable-compile will also disable native compilation.

Usually, straight.el uses symbolic links (“symlinks”) to make package files available from the build directory. This happens when straight-use-symlinks is non-nil, the default. On Microsoft Windows, however, support for symlinks is not always available, so the default value of straight-use-symlinks is nil on that platform. That causes copying to be used instead, and an advice is placed on find-file to cause the copied files to act as symlinks if you try to edit them.

If you want to activate symlink-support on MS Windows 7, 8, or 10, you should ensure the following requirements:

Customizing how packages are made available

By setting the variable straight-cache-autoloads to a non-nil value, you can cause straight.el to cache the autoloads of all used packages in a single file on disk, and load them from there instead of from the individual package files if they are still up to date. This reduces the number of disk IO operations during startup from O(number of packages) to O(1), so it should improve performance. No other configuration should be necessary to make this work; however, you may wish to call straight-prune-build occasionally, since otherwise this cache file may grow quite large over time.

Hooks run by straight-use-package

Currently, straight-use-package supports four hooks:

The recipe format

The general format for a straight.el recipe is:

(package-name :keyword value :keyword value ...)

Note that if you wish to pass a recipe to straight-use-package, you will need to quote it. If you need to compute part of the recipe dynamically, use backquoting:

(straight-use-package
  `(el-patch :type git :repo ,(alist-get 'el-patch my-package-urls)))

The supported keywords are similar, but not identical to those used in MELPA recipes. There is a complete list below which you can compare with the MELPA documentation, but the main differences from the user’s point of view are:

Here is a comprehensive list of all keywords which have special meaning in a recipe (unknown keywords are ignored but preserved):

(example :build nil)
(example :build t)
(example :build (autoloads compile native-compile info))
(example :build (:not compile info))

Steps may be disabled globally for recipes which do not explicilty declare their :build via the defcustom variables named straight--build-SYMBOL. e.g. The last example but for all recipes without a :build:

(setq straight-disable-compile t
      straight-disable-info t)

In the absence of a :build keyword, straight--build-default-steps are run.

("executable" "arg"...)

Commands are executed in the package’s repository directory.

The :pre-build keyword’s value may be:

(straight-use-package
 '( example :type git :host github :repo "user/example.el"
    :pre-build ("make" "all")))

(straight-use-package
 `( example :type git :host github :repo "user/example.el"
    :pre-build ,(pcase system-type
                  (`windows-nt '(message "This might take a while"))
                  (_ '(("./configure") ("make") ("make" "install"))))))

(straight-use-package
 '( example :type git :host github :repo "user/example.el"
    :pre-build  (("./pre-build.sh") (message "hi"))
    :post-build (("./post-build.sh") (message "bye"))))
(straight-use-package '(org :type built-in))

You can also use :type nil, which has the same effect as :type 'built-in, except that the package is still loaded from its configured :local-repo.

Overrides straight-recipe-repositories on a per-recipe basis. Its value may be:

(straight-use-package '(package :source melpa))

Will search only the melpa recipe repository for package’s recipe. While:

(straight-use-package '(package :source (melpa gnu-elpa-mirror)))

will search for package’s recipe first in melpa. If it is not found there it will check gnu-elpa-mirror next.

Informs straight.el that a package is a superset of another package. For example org-contrib includes ol-vm. The following will prevent straight.el from attempting to install ol-vm after org-contrib has been installed:

(straight-use-package '(org-contrib :includes ol-vm))

Its value may also be a list of symbols indicating multiple packages:

(straight-use-package '(example :includes (foo bar)))

Overrides straight-allow-recipe-inheritance on a per-recipe basis. If its value is non-nil, inheritance is enabled for the recipe. Otherwise it is not.

Version-control backends

Defining a version-control backend consists of declaring a number of functions named as straight-vc-BACKEND-METHOD, where BACKEND is the name of the version-control backend being defined and METHOD is a backend API method. The relevant methods are:

Most of these methods are highly interactive: they don’t actually do anything without prompting you to confirm it, and very often they will offer you a number of different options to proceed (including starting a recursive edit and allowing you to do whatever you would like).

Also, all of the methods in this section take straight.el-style recipes; see the section on defining VC backends in the developer manual for more details.

Git backend

These are the keywords meaningful for the git backend:

'((github    . "githubUser")
  (gitlab    . "gitlabUser")
  (codeberg  . "codebergUser")
  (sourcehut . "sourcehutUser")
  (bitbucket . "bitbucketUser")))

Its value may be:

( :package "package" :host github :type git :repo "upstream/repo"
  :fork t)

computes the fork’s :repo value as githubUser/repo.

( :package "package" :host github :type git :repo "upstream/repo"
  :fork "user")

computes the fork’s :repo value as user/repo.

( :package "package" :host github :type git :repo "upstream/repo"
  :fork "/renamed")

computes the fork’s :repo value as githubUser/renamed.

( :package "package" :host github :type git :repo "upstream/repo"
  :fork "user/renamed")

computes the fork’s :repo value as user/renamed.

( :package "package" :host github :type git :repo "upstream/repo"
  :fork (:host gitlab))

computes the fork’s :repo value as gitlabUser/repo.

( :package "package" :host github :type git :repo "upstream/repo"
  :fork (:host gitlab :repo "/renamed"))

computes the fork’s :repo value as gitlabUser/renamed.

( :package "package" :host github :type git :repo "upstream/repo"
  :fork (:host gitlab :repo "user"))

computes the fork’s :repo value as user/repo.

This section tells you how the git backend, specifically, implements the version-control backend API:

You can customize the following user options:

Deprecated :upstream keyword

straight.el previously supported fork configuration in recipes using an :upstream keyword rather than a :fork keyword. For various reasons, this was more complex to handle, which is why the change was made. For backwards compatibility, the :upstream keyword is still accepted, with the following behavior.

When straight.el processes a recipe which uses the :upstream keyword, it moves the :repo, :host, and :branch keywords from that sub-plist to the top level, and moves those top-level keywords to a new :fork sub-plist. Then it sets the top-level and :fork sub-plist values of :remote to the values of the deprecated variables straight-vc-git-upstream-remote (defaults to “upstream”) and straight-vc-git-primary-remote (defaults to “origin”), respectively.

For backwards compatibility, if straight-vc-git-primary-remote differs from its default value of “origin”, then its value is used in place of straight-vc-git-default-remote-name.

Recipe lookup

If you only provide a symbol (package name) to straight-use-package, then the recipe is looked up automatically. By default, MELPA, GNU ELPA, and Emacsmirror are searched for recipes, in that order. This means that one or more of them may need to be cloned. Recipe repositories are actually just the same as ordinary packages, except that their recipes specify :build nil, so they are not symlinked or added to the load-path.

Note that dependencies always use the default recipes, since the only information straight.el gets about a package’s dependencies are their names.

This leads to a few interesting questions regarding requesting a package multiple times. For example, you might need to load two features using use-package that are provided from the same package, or one of the packages you have installed is also requested as a dependency by another package. straight.el uses a number of heuristics to try to make these interactions as intuitive and painless as possible:

Updating recipe repositories

As mentioned in the conceptual overview, recipe repositories are just regular packages, with some extra code to look up recipes in the relevant local repository.

This means that updating a recipe repository may be done the same way as updating a regular package, i.e. with M-x straight-pull-package. A convenience command with interactive completion for recipe repositories, straight-pull-recipe-repositories, is provided as well. You should use one of these if you find that a package isn’t listed by M-x straight-use-package—perhaps it was added recently.

Note that there is currently some potentially surprising behavior if you update all packages at once using M-x straight-pull-all or M-x straight-merge-all, and this bulk update includes recipe repository updates: see #323.

Customizing recipe repositories

The recipe repository system is designed to be extended. Firstly, you can control which recipe repositories are searched, and in what order of precedence, by customizing straight-recipe-repositories. The default value is:

(org-elpa melpa gnu-elpa-mirror el-get emacsmirror)
GNU ELPA

You can customize the following user options:

(org-elpa melpa gnu-elpa el-get emacsmirror)
Emacsmirror

You can customize the following user option:

(org-elpa melpa gnu-elpa-mirror el-get emacsmirror-mirror)
Defining new recipe repositories

To define a new recipe repository called NAME, you should do the following things:

'`( package :type git :repo "host/repo"
    :pre-build ,(pcase system-type
                  (`berkeley-unix '("gmake"))
                  (_ '("make")))
    :files (:defaults))

The recipe is converted to:

(package :type git :repo "host/repo"
 :pre-build ("make")
 :files (:defaults))

on a gnu/linux system, and:

(package :type git :repo "host/repo"
         :pre-build ("gmake")
         :files (:defaults))

on a berkely-unix system.

The recipe could be read from a file in the recipe repository as well. In this case, the quote is not included in the recipe, as straight-recipes-NAME-retrieve would make use of read, which will return the literal Lisp object. For example, considering the following retrieval function:

(defun straight-recipes-example-retrieve (name)
  (with-temp-buffer
    (insert-file-literally "./recipes/example.recipe")
    (read (buffer-string))))

The recipe from above could be stored in the file, example.recipe, as:

`( package :type git :repo "host/repo"
   :pre-build ,(pcase system-type
                 (`berkeley-unix '("gmake"))
                 (_ '("make")))
   :files (:defaults))

Overriding recipes

You can always use straight-register-package to specify a specific recipe for a package without cloning or building it, so that just in case that package is requested later (possibly as a dependency, or in somebody else’s code) your recipe will be used instead of the default one. However, this does not help in the case that a specific recipe is passed to straight-use-package.

Also, it is obviously impossible to call straight-register-package before straight.el has been loaded, so you can’t use it to specify a custom recipe for straight.el itself.

To remedy these difficulties, straight.el provides a mechanism for specifically overriding the recipe for a particular package. You can use it by customizing straight-recipe-overrides, or by calling straight-override-recipe.

straight-recipe-overrides is an association list from profile names to override alists. If you don’t care about the profile system, you can just use a single override specification, with the profile name nil. Each override alist is just a list of recipes. Because the car of a recipe is just the package name as a symbol, this list of recipes is also an alist whose keys are recipe names and whose values are the plists for those recipes.

Even if an explicit recipe is supplied to straight-use-package, the one given in straight-recipe-overrides will be used instead, if such a recipe is specified there.

For convenience, you may add to straight-recipe-overrides by passing a recipe to straight-override-recipe. This will register it in the override alist for the current profile. Note that if you do this, you will probably want to explicitly set straight-recipe-overrides to nil before bootstrapping straight.el. This will make it so that if you remove a call to straight-override-recipe from your init-file and then reload it, the entry will actually be removed from straight-recipe-overrides.

Overriding the recipe for straight.el

As was briefly mentioned earlier, you can actually override the recipe of straight.el itself using straight-recipe-overrides! How does this work? Well, it’s basically black magic. If you want the details, go read the developer manual. All you need to know is that you can set straight-recipe-overrides, and it will magically work. The only caveat is that if you change the :local-repo for straight.el, then you will also need to adjust the value of bootstrap-file in the bootstrap snippet accordingly, since otherwise your init-file will not know where to find straight.el. (You must use straight-recipe-overrides instead of straight-override-recipe, since the latter function definition hasn’t been loaded yet before straight.el is installed and bootstrapped.)

Here is the default recipe used for straight.el, if you don’t override it:

(straight :type git :host github
          :repo ,(format "%s/straight.el" straight-repository-user)
          :files ("straight*.el")
          :branch ,straight-repository-branch)

Note that even though the bootstrap snippet references the develop branch of straight.el, the default recipe installs from master.

If all you want to do is change which branch you are installing straight.el from, simply customize the variable straight-repository-branch, which is provided for this purpose. (Although using straight-recipe-overrides will work just as well, at least until the recipe happens to be changed upstream and your init-file isn’t updated.)

Similarly, if all you want to do is switch to your own fork of straight.el on GitHub, simply customize the variable straight-repository-user to your GitHub username.

There is one minor caveat to the above discussion. If your fork makes changes to the way in which recipes are interpreted, then those changes will not be effective during the interpretation of your own recipe for straight.el. If you wish for them to be, then you will have to follow the same procedure that is followed in straight.el itself for making changes to recipe interpretation. These details are outlined in the developer manual; see also install.el for an explanation of this aspect of the bootstrap mechanism.

Interactive usage

The primary usage of straight.el is expected to be in your init-file. For example, this is where you will need to put the bootstrap code as well as any packages that you always want to be installed. However, there are three important interactive uses of straight.el: temporary installation of packages, various helpful utility functions, and version control operations.

To install a package temporarily, run M-x straight-use-package. All registered recipe repositories will be cloned, and you will be presented with a combined list of all recipes available from them. Simply select a package and it will be cloned, built, and loaded automatically. This does not affect future Emacs sessions.

If you provide a prefix argument to M-x straight-use-package, then you are presented with a list of registered recipe repositories. After you select one, you are shown a list of recipes specifically from that recipe repository. This is helpful if you do not want to clone all registered recipe repositories, or you have a particular recipe repository in mind.

You can also call M-x straight-get-recipe, which has the same interface as M-x straight-use-package, except that instead of the package being cloned, built, and loaded, its recipe is copied to the kill ring. If you are writing a custom recipe, this may be helpful, because you may be able to reuse parts of the existing recipe, particularly the :files directive.

Normally, packages are rebuilt automatically if needed, when Emacs restarts. If you for some reason want them to be rebuilt at another time, you can call M-x straight-check-all to rebuild all packages that have been modified since their last build. Alternatively, use M-x straight-rebuild-all to unconditionally rebuild all packages. Note that this will probably take a while. There are also M-x straight-check-package and M-x straight-rebuild-package, which allow you to select a particular package to check or rebuild.

Finally, you may use M-x straight-prune-build in order to tell straight.el to forget about any packages which were not registered since the last time you loaded your init-file. This may improve performance, although only slightly, and will clean up stale entries in the build directory. You can call this function in your init-file if you really wish your filesystem to be as clean as possible, although it’s not particularly recommended as the performance implications are uninvestigated. If you do call it in your init-file, be sure to only call it on a fully successful init; otherwise, an error during init will result in some packages’ build information being discarded, and they will need to be rebuilt next time.

If you have enabled autoloads caching, it is advisable to call straight-prune-build occasionally, since otherwise the build cache may grow quite large over time.

Version control operations

straight.el provides a number of highly interactive workflows for managing your package’s local repositories, using the configured version-control backends. They are as follows:

See the sections on version-control backends and the Git backend in particular for more information about the meanings of these operations.

Lockfile management

straight.el determines your package management configuration from two, and only two, sources: the contents of your init-file, and your version lockfiles (which are optional). Your init-file specifies the configuration of straight.el (for example, the values of straight-recipe-overrides and straight-default-vc), the packages you want to use, and their recipes. Your version lockfiles specify the exact revisions of each package, recipe repository, and even straight.el itself. Together, they lock down your Emacs configuration to a state of no uncertainty: perfect reproducibility.

To write the current revisions of all your packages into version lockfiles, run M-x straight-freeze-versions. This will first check that straight.el has an up-to-date account of what packages are installed by your init-file, then ensure that all your local changes are pushed (remember, we are aiming for perfect reproducibility!). If you wish to bypass these checks, provide a prefix argument.

Note: reloading your init-file must have the effect of running all of the same straight.el-related functions again. For example, if you bootstrap straight.el in a sub-file that you only require instead of load, then the reloading functionality will not work correctly and you may receive the message Caches are still outdated; something is seriously wrong. See #437 for discussion.

Version lockfiles are written into ~/.emacs.d/straight/versions. By default, there will be one, called default.el. It is recommended that you keep your version lockfiles under version control with the rest of your Emacs configuration. If you symlink your init-file into ~/.emacs.d from somewhere else, you should also make sure to symlink your version lockfiles into ~/.emacs.d/straight/versions. On a new machine, do this before launching Emacs: that way, straight.el can make sure to check out the specified revisions of each package when cloning them for the first time.

To install the versions of the packages specified in your version lockfiles, run M-x straight-thaw-versions. Thawing will interactively check for local changes before checking out the relevant revisions, so don’t worry about things getting overwritten.

The profile system

straight.el has support for writing multiple version lockfiles, instead of just one. Why? Consider a large Emacs configuration such as Radian, Spacemacs, or Prelude, which is used by many different people. There are two parts to the configuration that is actually loaded: the “default” part, and the local customizations that each user has added. Generally, these configurations have a mechanism for making local customizations without forking the entire project.

So Radian will have some set of packages that it requires, and my local customizations of Radian have some other set of packages that they require. In order for me to maintain Radian, I need to be able to separate Radian’s packages (which go into a versions lockfile in the Radian repository) from my own local packages (which go into a versions lockfile in my own private local dotfiles repository). straight.el provides this ability through the profile system.

The idea is that whenever a package is registered, either directly or as a dependency, it is associated with a given profile. Any given package can be associated with multiple profiles.

When you call straight-use-package, which profile the registered packages are associated with is determined by the value of straight-current-profile, which defaults to nil. In Radian, for example, straight-current-profile is bound to radian while the Radian libraries are being loaded, and it is bound to radian-local while the user’s local customizations are being loaded. This results in Radian packages being associated with the radian profile, and the user’s local packages being associated with the radian-local profile.

When you call M-x straight-freeze-versions, one or more version lockfiles are written, according to the value of straight-profiles. This variable is an association list whose keys are symbols naming profiles and whose values are filenames for the corresponding version lockfiles to be written into ~/.emacs.d/straight/versions. You should make sure that each potential value of straight-current-profile has a corresponding entry in straight-profiles, since otherwise some packages might not be written into your lockfiles.

When customizing straight-recipe-overrides, note that if multiple profiles are set to override the same recipe, then the last one listed in straight-profiles will take precedence. Similarly, when using M-x straight-thaw-versions, if different lockfiles specify revisions for the same local repository, the last one in straight-profiles will take precedence.

Packages and the init-file

Package managers like package.el store mutable state outside your init-file, including the set of packages that are installed. straight.el does not do this, so it has a rather different way of determining what packages are installed. To straight.el, a package is part of your Emacs configuration if it is passed to straight-use-package when your init-file is loaded.

Note that this means packages installed interactively (using M-x straight-use-package) are not considered part of your Emacs configuration, since the invocation of straight-use-package does not happen in your init-file.

This raises an interesting question: if you add a package to your init-file, how can you convince straight.el that it really is part of your init-file, and not just part of a temporary straight-use-package form that you evaluated ad-hoc? The answer is simple: reload your entire init-file. That way, straight.el will see whether or not that package is registered during your init-file.

One might ask how straight.el determines that you have finished loading your init-file. The answer is simple: post-command-hook is used to execute code only after the current interactive operation has finished. The implementation of this concept is part of the transaction system of straight.el, and it is also used to amortize certain performance costs when many calls to straight-use-package are made sequentially. However, since the transaction system (at least in recent versions of straight.el) operates transparently, its details are relegated to the developer manual.

Using straight.el to reproduce bugs

… in other packages

One of the major reasons I wanted to write straight.el was that existing package managers were not good for reproducing bugs. For instance, some of them would load all installed packages when the package manager was initialized! Obviously that is not acceptable for a “minimal test case”.

On the contrary, bootstrapping straight.el does not load anything except for straight.el itself (the default recipe repositories are registered, but not cloned until needed). You should normally be loading straight.el by means of the bootstrap snippet, but when you are in emacs -Q, here is how you can initialize straight.el:

M-x load-file RET ~/.emacs.d/straight/repos/straight.el/bootstrap.el RET

You can also do this from the command line, perhaps by creating an alias for it:

$ emacs -Q -l ~/.emacs.d/straight/repos/straight.el/bootstrap.el

Let’s say you are making a bug report for Projectile. To load just Projectile and all of its dependencies, run:

M-x straight-use-package RET projectile RET

Note that this will use the currently checked-out revisions of Projectile and all of its dependencies, so you should take note of those in order to make your bug report.

… in straight.el itself

straight.el provides a macro, straight-bug-report, to test straight.el in a clean environment. If possible, please use this when creating bug reports.

straight-bug-report accepts the following keyword value pairs:

(setq straight-repository-branch "develop")
Note this example is already in the default bootstrapping code.
(straight-use-package '(example :type git :host github))

For example:

(straight-bug-report
  :pre-bootstrap
  (message "before bootstrap")
  (message "multiple forms allowed")
  :post-bootstrap
  (message "after bootstrap")
  (message "multiple forms allowed")
  (straight-use-package '(my-broken-package))
  (message "bye"))

The above will run your test in a clean environment and produce a buffer with information you can paste directly into the issue body.

Using straight.el to develop packages

The workflow for developing a package using straight.el is quite straightforward:

Integration with other packages

Integration with use-package

By default, straight.el installs a new keyword :straight for use-package which may be used to install packages via straight.el. The algorithm is extremely simple. This:

(use-package el-patch
  :straight t)

macroexpands (essentially) to:

(straight-use-package 'el-patch)

And this:

(use-package el-patch
  :straight (:host github :repo "radian-software/el-patch"
             :branch "develop"))

becomes:

(straight-use-package
 '(el-patch :host github :repo "radian-software/el-patch"
            :branch "develop"))

If the feature you are requiring with use-package is different from the package name, you can provide a full recipe:

(use-package tex-site
  :straight (auctex :host github
                    :repo "emacsmirror/auctex"
                    :files (:defaults (:exclude "*.el.in"))))

And you may also provide just the package name:

(use-package tex-site
  :straight auctex)

If you don’t provide :straight, then by default nothing happens. You may customize straight-use-package-by-default to make it so that :straight t is assumed unless you explicitly override it with :straight nil.

Previously, straight.el used a different syntax for its use-package integration. For backwards compatibility, you can use this syntax instead by customizing straight-use-package-version.

You can disable use-package integration entirely by customizing straight-enable-use-package-integration.

Loading packages conditionally

use-package has various features intended to support code being executed conditionally for a package. For example, the :when keyword lets you provide a form that will essentially disable the use-package form if it evaluates to nil.

However, when using the :straight keyword, either explicitly or via straight-use-package-by-default, then :when has no effect on it. straight.el is invoked unconditionally. The reason for this behavior is that if you invoke straight-use-package on a different set of packages during different init sessions, then your version lockfile would end up containing different sets of packages depending on which session you generated it in.

Currently, the officially recommended pattern for conditionally loading a package is the following:

(straight-register-package 'foobar)
(when some-condition
  (use-package foobar
    :straight t))

This ensures that the package is registered to straight.el, so it will be cloned if absent, and will be added to the lockfile, but it will not be compiled or loaded unless the subsequent use-package form is evaluated. You can also invoke straight-register-package only in the case that some-condition is nil; either way will produce the same result with roughly the same performance due to idempotency and caching.

If you do this for a lot of packages, it may be advisable to wrap it in a macro, as my own Emacs configuration Radian does in the macro radian-use-package. It would be a good idea if straight.el did this by default in its use-package integration but this has not been implemented yet.

If you want to not even clone a package when it is disabled, you can also technically achieve it by simply making the entire use-package form conditional, without using straight-register-package. However, this is not recommended because it will cause the generated lockfile to be deterministic, so straight.el will not be changed to make :when act that way by default.

It would be desirable if you could clone a package conditionally without breaking the lockfile functionality; this is a hopefully planned future feature, but it needs design work.

“Integration” with package.el

By default, package.el will automatically insert a call to package-initialize into your init-file as soon as Emacs starts, which is ridiculous. It will also do this when you perform any package management operation. A separate system inserts some custom forms into your init-file when you install a package. straight.el disables all of these “features” by setting package-enable-at-startup to nil and enabling some advices. You can override this behavior by customizing straight-enable-package-integration, however.

To help avoid you shooting yourself in the foot by using both :ensure and :straight at the same time in a use-package form (which would cause the same package to be installed twice using two different package managers), straight.el will helpfully disable :ensure whenever you include :straight in a use-package form. See #425.

Integration with Flycheck

Flycheck sometimes creates temporary files in order to perform syntax checking. This is a problem for straight.el because creation of temporary files will cause straight.el to think that you have modified a package when you actually have not. (You might ask why straight.el cannot recognize temporary files and ignore them. The answer is that for eager modification checking, all we see is that the directory mtime for the repository has been updated, and there’s no way to disambiguate between temporary file shenanigans versus if you, say, deleted a file.)

To work around the problem, a user option straight-fix-flycheck is provided, disabled by default (for now). You can enable it before loading straight.el, and it will work around the Flycheck problem in the following way. When you first visit a buffer, any Flycheck checker that involves creation of temporary files will be inhibited automatically, although other checkers will still run. (In practice this means no byte-compilation errors for Emacs Lisp, but you still get Checkdoc errors.) However, after you make a change to the buffer (by typing, etc.) then all checkers will be re-enabled. This means that straight.el won’t think the package was modified unless you actually modify the buffer of a file inside it, which I think is a reasonable compromise.

See #508 for discussion.

Integration with Hydra

See the Hydra wiki.

Miscellaneous

Looking for cider recipe → Cloning melpa...

If your terminal does not support Unicode characters nicely, you can customize straight-arrow to display something else for the arrow.

Developer manual

This section tells you about all the interesting implementation details and design decisions that power straight.el behind the scenes. It assumes you have already read the user manual and the conceptual overview.

More to be written here in future. See #51.

Low-level functions

Trivia

This section has random, (possibly) interesting tidbits about straight.el that don’t fit in the other sections.

Comments and docstrings

How did I get that statistic about the percentage of straight.el that is comments and docstrings? Simple: by abusing the syntax highlighting.

(let ((lines (make-hash-table :test #'equal)))
  (goto-char (point-min))
  (while (< (point) (point-max))
    (when (memq (face-at-point)
                '(font-lock-comment-face
                  font-lock-doc-face))
      (puthash (line-number-at-pos) t lines))
    (forward-char))
  (* (/ (float (length (hash-table-keys lines)))
        (line-number-at-pos))
     100))

Note that you will have to scroll through the entire buffer first, since font-lock-mode computes syntax highlighting lazily.

Contributing

Please do! Development takes place on the develop branch. You can switch to that branch with

(setq straight-repository-branch "develop")

and base your pull requests from it. If you have an outstanding pull request whose features you would like to use in your configuration, there is full support for defining straight.el as coming from any branch in any fork:

(setq straight-repository-user "my-github-username")
(setq straight-repository-branch "feat/my-cool-feature")

For additional information, please see the contributor guide for my projects. Note that straight.el has not yet had an initial release, so you don’t have to worry about a changelog.

FAQ

My init time got slower

Your first step should be to customize the value of straight-check-for-modifications. The best setting is (watch-files find-when-checking); this is not enabled by default because it is impolite to spawn persistent background processes without asking, and because you must install Python 3 and watchexec for it to work. If you can’t stand the extra dependencies and background process, consider the setting (check-on-save find-when-checking) instead, which is just as fast but won’t catch package modifications unless they are made within Emacs via the save-buffer command.

Even with lazy modification detection, as described above, straight.el is not quite as fast as package.el (by a few percentage points). There are some planned changes which will make straight.el just as fast as package.el, if not faster. See #9.

“Could not find package in recipe repositories”

Assuming that the package you’re trying to install actually exists, you need to update your recipe repositories (most likely MELPA, possibly Emacsmirror). See the next FAQ entry. This is like running package-refresh-contents under package.el.

Another possibility is that you are providing straight.el with a feature name rather than a package name. Features are what you load with require or load, or find in files. For example, org-agenda and org-capture are features. Packages, on the other hand, can provide one or more features. They are what are listed on MELPA et al. or by M-x straight-get-recipe. For example, org and org-contrib are packages.

When you write (use-package foo ...), the foo is a feature, not a package. You can give a different package name bar by saying (use-package foo :straight bar). And when you write (straight-use-package 'bar), the bar is a package, not a feature.

How do I update MELPA et al.?

Using M-x straight-pull-package, like for any other package. Read more.

My use-package form isn’t working properly

There are a number of common problems you might be encountering. Check out the following list to see if there is an easy fix.

How do I uninstall a package?

My first question is: do you really need to uninstall the package? Under package.el, every package on disk gets loaded into Emacs, whether you asked for it or not. However, under straight.el, only the packages you explicitly mention in your init-file get loaded into Emacs. So the only problem with leaving a package on disk is that it takes up a little bit of disk space. (But the advantage is if you decide you want to use that package again later then you won’t have to redownload it.)

If you really want to uninstall a package, simply delete its local repository from ~/.emacs.d/straight/repos or run the straight-remove-unused-repos command.

The wrong version of my package was loaded

To explain this problem, let us consider a concrete example. In this issue, a user found that the code

(straight-use-package 'company-lsp)
(straight-use-package 'eglot)

sometimes resulted in runtime errors because an old version of Flymake was being used.

The root problem here is that you want the most recent version of Flymake to be installed by straight.el, but Emacs also ships an older version, and that older version is getting loaded instead.

The older version will be loaded if (require 'flymake) (or similar) is invoked before straight.el has made Flymake available (by means of (straight-use-package 'flymake) or similar). But why would straight.el not make Flymake available?

The only way that straight.el knows to make Flymake available is if either you manually invoke straight-use-package in your init-file, or if one of the packages that you request in your init-file declares Flymake as a dependency. Now, any package that uses Flymake ought to declare it as a dependency. Thus, there should be no way for a package to load the Emacs-provided version of Flymake. However, sometimes package authors overlook this problem (it does not always cause an error, and sometimes package authors do not test exhaustively enough).

In this case, the problem was that company-lsp declared a dependency on lsp-mode, and lsp-mode used Flymake without declaring a dependency on flymake. There are two ways to work around the problem:

If you test this yourself, you might find it difficult to reproduce the problem. That is because there is only an issue when Flymake is actually loaded, and this doesn’t necessarily happen when invoking (straight-use-package 'company-lsp) unless straight.el needs to rebuild the relevant packages (which includes byte-compilation, which sometimes means actually loading dependencies). Keep this in mind when testing.

This problem commonly occurs with Org, since (1) Org is popular, (2) Emacs ships an obsolete version of Org, (3) many users want to use the up-to-date version, and (4) Org breaks backwards compatibility frequently. To solve it, simply make sure that you invoke (straight-use-package 'org) before running any code that could load Org, including installing any package that lists it as a dependency.

See this issue for discussion about ways of mitigating the bad UX of this situation.

I get “could not read username/password” errors

This is because straight.el is not currently able to detect when SSH or Git asks for your username and/or password/passphrase and then pipe that prompt through to the minibuffer (#334).

To work around the problem, set up git-credential-cache if you use HTTPS, and ssh-agent if you use SSH. That way, you won’t be prompted for your username/password. When setting up ssh-agent, be careful to make sure that the relevant environment variables get set in Emacs. This might be tricky since starting Emacs from the desktop (rather than from the command line) sometimes results in it not inheriting any environment variables from your shell.

How do I pin package versions or use only tagged releases?

This is a planned feature. In the meantime, contributors have proposed various workarounds. See #246 and #31.

straight-x.el now contains an experimental solution. In order to use it you will need to add similar snippets to your Emacs configuration.

First you need to add a new profile to straight-profiles which also needs to be the last profile in the list. This should be done before you bootstrap straight.el.

;; Tell straight.el about the profiles we are going to be using.
(setq straight-profiles
      '((nil . "default.el")
        ;; Packages which are pinned to a specific commit.
        (pinned . "pinned.el")))

After straight’s install procedure you will need to add straight-x.el and load the required commands.

(autoload #'straight-x-pull-all "straight-x")
(autoload #'straight-x-freeze-versions "straight-x")

A variable called straight-x-pinned-packages has been defined in straight-x.el and will contain your list of pinned packages.

From now on, you can pin a package to a specific commit like in the following example which will pin org-mode to the 9.2.3 release version:

(let ((straight-current-profile 'pinned))
  (straight-use-package 'org)
  (straight-use-package 'org-contrib)
  ;; Pin org-mode version.
  (add-to-list 'straight-x-pinned-packages
               '("org" . "924308a150ab82014b69c46c04d1ab71e874a2e6")))

If you invoke straight-x-freeze-versions it will first write the default lockfile and then pinned lockfile which takes precedence over the default one if packages are thawed. straight-x-pull-all will first invoke straight-pull-all and then restore all pinned packages.

You might want to assign the following aliases for more convenience:

(defalias 'straight-pull-all #'straight-x-pull-all)
(defalias 'straight-freeze-versions #'straight-x-freeze-versions)

Please keep in mind that this is only a temporary solution and experimental!

How can I use the built-in version of a package?

To tell straight.el that you want to use the version of Org shipped with Emacs, rather than cloning the upstream repository:

(straight-use-package '(org :type built-in))

Note that :type is a keyword for straight.el, not for use-package. If you are using use-package, then use:

(use-package org :straight (:type built-in))

Read more.

News

Jan 1, 2021

Breaking change: The previous behavior of the :build keyword is now associated with the :pre-build keyword. :build is now used to specify build steps (generating autoloads and texinfo, byte/native compilation, etc). For more information on both of these keywords see the recipe format.

The following customization variable names have changed:

April 19, 2020

Shallow clones are now compatible with lockfiles, so you can safely set straight-vc-git-default-clone-depth to 1 and get massive savings on network bandwidth and disk space.