I have this by default in my auto-mode-alist:

("\\.js\\'" . javascript-mode)

(even with emacs -Q). I'd like to substitute js2-mode for javascript-mode. Of course, I could use assq-delete-all and then add-to-list again, but I'm wondering whether there isn't a better way.

Edit: I explicitly do not want to use Customize, I prefer crafting my init.el myself.

6 Answers 6


While @Dan's answer is a perfectly fine solution, it is unnecessary. One of the reasons Emacs uses an alist here is that with an alist you can simply add a new element to the front of the list and it will shadow matches further down the list.

(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
  • 11
    Admittedly, it does not answer the question of how to replace. Its point is that you do not need to replace (unless you have some other need than what you described).
    – Drew
    Commented Nov 11, 2014 at 22:19
  • 1
    Exactly. Still, in my book this counts as a solution (even if I do not like it).
    – mbork
    Commented Nov 11, 2014 at 22:27
  • 1
    +1. It's the less-convoluted solution for working with this alist than the setf option, but the latter gets to the question of how to replace elements rather than shadow them.
    – Dan
    Commented Nov 11, 2014 at 23:12
  • 2
    the question wasn't really about replacing in the technical sense but rather changing in the higher-level sense. Commented Nov 12, 2014 at 0:43
  • 2
    This will always work no matter if the cons is in pure space or will be removed in some future version.
    – politza
    Commented Nov 30, 2016 at 20:52

Use setf to change the value in place:

(setf (cdr (rassoc 'javascript-mode auto-mode-alist)) 'js2-mode)

If you want to replace a value in the list, then setf is the generalized machinery you need to do so. For the more idiomatic way to deal with the auto-mode-alist, see @Drew's answer (and his explanation of shadowing).


The fastest way to actually change the cons cell is probably setcdr

setcdr is a built-in function in `C source code'.

(setcdr CELL NEWCDR)

Set the cdr of CELL to be NEWCDR.  Returns NEWCDR.

It's worth noting that setf isn't available in older Emacsen, but setcdr is.

*** Welcome to IELM ***  Type (describe-mode) for help.
ELISP> (setq tmp '((one . 1) (two . 2) (three . 4)))
((one . 1)
 (two . 2)
 (three . 4))

ELISP> (setcdr (assq 'three tmp) 3)
3 (#o3, #x3, ?\C-c)
ELISP> tmp
((one . 1)
 (two . 2)
 (three . 3))
  • Do you happen to know which version of Emacs added setf?
    – dshepherd
    Commented Nov 6, 2018 at 17:47
  • 1
    @dsheperd not off-hand, no. Why do you need to know? I would say any emacs that should be targeted for new development will have setf, but there might not be handling for it for the kind of data you want to set. They're called generalized variables. Commented Nov 6, 2018 at 20:10
  • I wanted to know if it was ok to use setf in some new code, but as you said it turns out there was no generalised variable for what I wanted until too recent a version anyway.
    – dshepherd
    Commented Nov 7, 2018 at 8:40

The OP asks for a solution which handles alists that have string keys. To handle that, see this question. If by chance you only need to handle alists with symbol keys, then as of Emacs 25 you can use:

(setf (alist-get <key> <alist>) <value>)

to replace a cdr. If you have access to Emacs 26, this technique does work with string keys, as follows:

(setf (alist-get "\\.js\\'" auto-mode-alist nil nil #'equal) 'js2-mode)

Note that there are also other ways in Emacs 26 to handle string keys; see this question as mentioned above.

  • (setf (alist-get "\\.js\\'" auto-mode-alist nil nil #'equal) 'js2-mode) should work (requires Emacs 26 though).
    – npostavs
    Commented Feb 3, 2018 at 13:45
  • @RadonRosborough: there is an edit feature. Consider to fix you answer.
    – antonio
    Commented Feb 27, 2018 at 21:41
  • You are using alist-get with the string "\\.js\\'", but alist-get is based on assq, so it won't work with a string as you claim in your answer.
    – antonio
    Commented Mar 2, 2018 at 5:22
  • @antonio Oh yes, you are entirely correct. I had not realized that the question as posted actually does require a solution which handles string keys. I'll make the edit, thanks! Commented Mar 2, 2018 at 19:34

If you know you won't use javascript-mode ever again let auto-mode-alist untouched and add to your init.el

  (defalias 'javascript-mode 'js2-mode "Some handy explanation goes here.")
  • 2
    Actually, there is no javascript-mode, really: javascript-mode is only an alias for js-mode (by default) and it was done this way specifically so that users could do like you suggest if they prefer js2-mode (without losing the ability to use js-mode if they want to).
    – Stefan
    Commented May 17, 2017 at 13:14
  • I derived my answer from habit of aliasing for cperl-mode and nxml-mode. So what would do the trick here? (defalias 'js-mode 'js2-mode)?
    – Matthias
    Commented May 17, 2017 at 13:22
  • 1
    You misunderstood me. I'm saying that your answer is exactly right and doesn't prevent you from using "javascript-mode" since what you call by that name is really js-mode (contrary to what happens for perl-mode, for instance).
    – Stefan
    Commented May 17, 2017 at 13:25
  • Got it... (casual javascript-mode user here)
    – Matthias
    Commented May 17, 2017 at 13:29

With the new seq.el library you can filter the alist.

E.g. we can seq-filter the auto-mode-alist. We return nil for the entries we wish to remove from the alist and t for the entries we want to stay.

 (lambda (x)
   (if (equal (car x) "\\.js\\'")

Then you set the auto-mode-alist value to this new filtered alist.

(setq auto-mode-alist (seq-filter
 (lambda (x)
   (if (equal (car x) "\\.js\\'")
     t)) auto-mode-alist))

Edit: Drew simplified the predicate in the comments:

(setq auto-mode-alist 
    (lambda (x)
      (not (equal (car x) "\\.js\\'"))) 
  • Aka (not (equal (car x) "\\.js\\'")).
    – Drew
    Commented Apr 7, 2022 at 16:28
  • Thanks. Yeah, that is simpler.
    – wos
    Commented Apr 7, 2022 at 18:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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