overleaf-project provides project-level Overleaf integration for Emacs:
- clone a full Overleaf project to a local Git repository
- keep editing locally with normal Git commits
- sync the committed project snapshot back to Overleaf
- resolve local/remote divergence with normal
git mergeon a dedicated branch
This package no longer provides the old single-buffer live editing workflow.
Existing configs can still use (require 'overleaf) through the
compatibility shim, but new configs should load overleaf-project
directly.
Add the package to your load path and require overleaf-project, or use
use-package:
(use-package overleaf-project
:config
(let ((cookie-file "~/.overleaf-cookies.gpg"))
(setq overleaf-save-cookies
(overleaf-save-cookies-to-file cookie-file))
(setq overleaf-cookies
(overleaf-read-cookies-from-file cookie-file))))The package targets Emacs 29.4+ and depends on the Elisp packages
plz, websocket and webdriver.
The package feature is named overleaf-project. Interactive commands
and customization variables keep the overleaf- prefix.
If you use a self-hosted Overleaf instance, set the default server URL:
(setopt overleaf-default-url "https://latex.example.edu")Project sync expects these executables to be available:
gitcurlunzip
Optional authentication through the browser also needs geckodriver.
You need valid Overleaf session cookies before cloning or syncing.
The easiest setup is to let Emacs open a Firefox webdriver session and store cookies after login:
(let ((cookie-file "~/.overleaf-cookies.gpg"))
(setq overleaf-save-cookies
(overleaf-save-cookies-to-file cookie-file))
(setq overleaf-cookies
(overleaf-read-cookies-from-file cookie-file)))Then run:
M-x overleaf-authenticate
If you already have a logged-in Firefox profile, you can read cookies directly:
(setq overleaf-cookies
(overleaf-read-cookies-from-firefox
:firefox-folder "~/.mozilla/firefox/"
:profile "your-profile-name"))If the live Firefox cookie database is locked, the loader retries by copying the database to a temporary location first.
You can also store cookies manually. The value must be an alist keyed by domain, for example:
(setq overleaf-cookies
'(("overleaf.com"
"overleaf_session2=[redacted]"
1893456000)))Run:
M-x overleaf-project-clone
This command:
- prompts for the Overleaf URL and project
- downloads the full project zip
- creates a local Git repository
- commits the imported snapshot
- stores Overleaf metadata in the repo’s local Git config
After cloning, work in the repo normally:
- edit files locally
- optionally stage only the changes you want to include
- run
M-x overleaf-project-sync
overleaf-project-sync now prepares the local branch before talking to
Overleaf:
- if there are staged changes, it commits them automatically
- if there are unstaged or untracked files, it asks whether to stage all changes first
- it then fetches the latest remote Overleaf snapshot, merges if needed, and syncs the final tree back to Overleaf
Sync still has to run from a normal branch rather than a detached
HEAD.
Run:
M-x overleaf-project-sync
The command compares three states:
- the last successfully synced Git commit
- the current local
HEAD - the latest project snapshot downloaded from Overleaf
Then it behaves as follows:
- if only local changed, it uploads
HEADto Overleaf - if only remote changed, it fast-forwards your current branch locally
- if both changed and the final tree already matches, it only updates the sync base
- if both changed differently, it creates a new sync branch and runs
git merge
The clone and sync commands store project metadata in the repository’s local Git config, so later syncs can run from anywhere inside that repo.
When both local and remote changed, overleaf-project-sync creates a
branch named like:
overleaf-sync/20260412-153000
It then merges the downloaded remote snapshot into that branch.
If the merge conflicts:
- the original branch is left untouched
- the sync branch stays checked out
- you resolve conflicts with Magit or plain Git
- after creating the merge commit, rerun
M-x overleaf-project-sync
This keeps conflict resolution entirely inside Git instead of ediff.
If you commit from Emacs via Magit / git-commit, you can trigger sync
automatically after each commit:
(with-eval-after-load 'git-commit
(add-hook 'git-commit-post-finish-hook
#'overleaf-project-sync-after-commit))The hook only syncs repositories that already contain Overleaf project metadata.
The main interactive commands are:
overleaf-authenticateoverleaf-project-cloneoverleaf-project-syncoverleaf-project-sync-after-commitoverleaf-browse-project
You can also bind the provided command map:
(global-set-key (kbd "C-c o") overleaf-command-map)The default bindings inside that map are:
a->overleaf-authenticateb->overleaf-browse-projectc->overleaf-project-clones->overleaf-project-sync
Useful options include:
overleaf-default-urlfor self-hosted Overleaf instancesoverleaf-cache-cookiesto force cookie reloads instead of cachingoverleaf-debugfor verbose loggingoverleaf-project-sync-auto-commit-messagefor the automatic local checkpoint commitoverleaf-project-sync-branch-prefixto rename conflict branchesoverleaf-project-socket-timeoutif project tree websocket fetches need longeroverleaf-git-executable,overleaf-curl-executableandoverleaf-unzip-executableif the tools are not on yourPATH
- Sync currently recreates changed remote files instead of preserving old Overleaf document ids. That is acceptable for the new project-level workflow, but it is different from the old live buffer integration.
- Empty directories are not tracked by Git and therefore are not preserved by sync.
- Verbose logging can be enabled by setting
overleaf-debugtot.