How could I make an interactive function to toggle in between two color themes?

From what I have been able to find there is no variable set to what color theme is currently in use, in fact several can be loaded simultaneously?

Also, in order to toggle you would need to first do disable-theme for the currently loaded theme, to not make themes clash.

How to do that without knowing which theme is currently loaded?


I have made it but I switch 3 themes (My own kosmos, leuven and default)

You can check https://github.com/habamax/.emacs.d/blob/master/lisp/haba-appearance.el

excerpt from it:

(defvar *haba-theme-dark* 'kosmos)
(defvar *haba-theme-light* 'leuven)
(defvar *haba-current-theme* *haba-theme-dark*)

;; disable other themes before loading new one
(defadvice load-theme (before theme-dont-propagate activate)
  "Disable theme before loading new one."
  (mapcar #'disable-theme custom-enabled-themes))

(defun haba/next-theme (theme)
  (if (eq theme 'default)
      (disable-theme *haba-current-theme*)
      (load-theme theme t)))
  (setq *haba-current-theme* theme))

(defun haba/toggle-theme ()
  (cond ((eq *haba-current-theme* *haba-theme-dark*) (haba/next-theme *haba-theme-light*))
        ((eq *haba-current-theme* *haba-theme-light*) (haba/next-theme 'default))
        ((eq *haba-current-theme* 'default) (haba/next-theme *haba-theme-dark*))))

Then bind some key to haba/toggle-theme.

I use emacs on 2 different machines and environments (day, late evening) so there is facility to save/restore current-theme on quit/load emacs. Which is handy :)


I wrote a few functions to cycle through a group of themes.

(setq ivan/themes '(elixir elixir-dark))
(setq ivan/themes-index 0)

(defun ivan/cycle-theme ()
  (setq ivan/themes-index (% (1+ ivan/themes-index) (length ivan/themes)))

(defun ivan/load-indexed-theme ()
  (ivan/try-load-theme (nth ivan/themes-index ivan/themes)))

(defun ivan/try-load-theme (theme)
  (if (ignore-errors (load-theme theme :no-confirm))
      (mapcar #'disable-theme (remove theme custom-enabled-themes))
    (message "Unable to find theme file for ‘%s’" theme)))

I call ivan/load-indexed-theme in my init file to initialize my theme.

I bind ivan/cycle-theme to Space\ in evil mode. (Space is my leader key.)


Even though the existing answers work fine, I would like to share a simpler one:

(defun toggle-theme ()
  (if (eq (car custom-enabled-themes) 'leuven)
      (disable-theme 'leuven)
    (enable-theme 'leuven)))
(global-set-key [f5] 'toggle-theme)

This does not disable the default custom theme first, but I like that.

  • This is so money! Simple, clean, effective. Sep 17 '20 at 1:18
  • One thing I noticed is if I have no theme enabled in my init, in other words I'm using the default theme, this does not work and reports "custom theme not found". Sep 18 '20 at 16:37
  • 1
    Changing enable-theme to load-theme solved this problem. Sep 22 '20 at 20:10

This is the module I wrote for my own .emacs to solve this problem. My basic approach seems to be similar in intent to Maxim Kim's solution (rotating through a list of themes), but I think mine is more modular and may therefore be more accessible to an outsider. On the other hand, I don't have any of Kim's persistence features.

Here is the relevant code, leaving off variable declarations and package commentary:

(require 'dash)

(defun multitheme--enable (theme)
  "As `enable-theme', but load the theme if necessary.
Respect `custom-safe-themes'."
  (if (custom-theme-p theme)
      (enable-theme theme)
    (load-theme theme)))

(defun multitheme-cycle ()
  "Cycle between the themes in `multitheme-base-theme-list'.
If none of these themes is currently active, instead enable the
first element of `multitheme-base-theme-list'.

Also re-enable `multitheme-overtheme' so it remains \"on top\" of
the base theme.

If a theme to be enabled is not yet defined, attempt to load it
first (using `load-theme').  Respect `custom-safe-themes'.

After all theme changes have been made, run
  (when (require 'validate nil :noerror)
    (validate-variable 'multitheme-base-theme-list)
    (validate-variable 'multitheme-overtheme)
    (validate-variable 'multitheme-base-theme-change-hook))
  (let ((themes (-drop-while
                 (lambda (thm) (not (custom-theme-enabled-p thm)))
    ;; Cycle base theme
    (if (null themes)
        (multitheme--enable (car multitheme-base-theme-list))
      (disable-theme (car themes))
      (multitheme--enable (or (cadr themes)
                              (car multitheme-base-theme-list))))
    ;; Reassert overtheme
    (when multitheme-overtheme
      (multitheme--enable multitheme-overtheme))
    ;; Run hooks
    (run-hooks 'multitheme-base-theme-change-hook)))

I have this setup for cycling themes:

(defvar quick-switch-themes
  (let ((themes-list (list 'jazz
                           ;; 'vicarie-and-blackboard
                           ;; 'tangotango
    (nconc themes-list themes-list))
  "A circular list of themes to keep switching between.
Make sure that the currently enabled theme is at the head of this
list always.

A nil value implies no custom theme should be enabled.")

(defun quick-switch-themes* ()
  "Switch between to commonly used faces in Emacs.
One for writing code and the other for reading articles."
  (if-let* ((next-theme (cadr quick-switch-themes)))
      (progn (when-let* ((current-theme (car quick-switch-themes)))
               (disable-theme (car quick-switch-themes)))
             (load-theme next-theme t)
             (message "Loaded theme: %s" next-theme))
    ;; Always have the dark mode-line theme
    (mapc #'disable-theme (delq 'smart-mode-line-dark custom-enabled-themes)))
  (setq quick-switch-themes (cdr quick-switch-themes)))

I know I'm a little late to the party but I created a package to do exactly that, and much more.

It basically lets you define a list of your favorite color themes (which is optional) and move through the list conveniently.

  • What does this do that narendraj9's answer does not do
    – mmmmmm
    Oct 13 '20 at 17:13
  • It started with a few convenience functions to help you set a set of favorite themes and switch through them on demand but then later has been added with ivy integration and more. It also provides other features like a hook when a theme is switched to using theme-looper and a few more.
    – myTerminal
    Oct 13 '20 at 18:14
  • A fresh testimonial :) github.com/myTerminal/theme-looper/issues/14#issue-737899225
    – myTerminal
    Nov 6 '20 at 17:52

New Emacsen have customize-themes function made to support exactly this, to switch the themes easily.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.