Recentf is a minor mode that builds a list of recently opened files. This list is is automatically saved across sessions on exiting Emacs - you can then access this list through a command or the menu.

To enable ‘recentf-mode’, put this in your `~/.emacs’:

(recentf-mode 1)
(setq recentf-max-menu-items 25)
(setq recentf-max-saved-items 25)
(global-set-key "\C-x\ \C-r" 'recentf-open-files)

This mode has been part of GNU Emacs since version 21.

Periodically saving the list of files

By default, Recentf saves the list of recent files on exiting Emacs (specifically, `recentf-save-list` is called on `kill-emacs-hook`). If Emacs exits abruptly for some reason the recent file list will be lost - therefore you may wish to call `recentf-save-list` periodically, e.g. every 5 minutes:

    (run-at-time nil (* 5 60) 'recentf-save-list)

Recentf Filters (Structured Menu)

Recentf lets you structure its menus in various ways, using what it calls filters. This section describes ways to take advantage of that menu organization at the keyboard, with completion.

A La Carte

You can take advantage of recentf filters (that is, structured menus) from the keyboard (with completion and so on) by using library LaCarte.

Assuming that you have ‘ESC ESC x’ (that is, ‘ESC M-x’) bound to ‘lacarte-execute-menu-command’ (which you should ;-)), just type part of a recent file name (any part, or a regexp), then hit ‘S-TAB’ to complete to matching recent files.

If you use, say, the recentf filter ‘recentf-arrange-by-dir’, then the File > Open Recent menu is organized by directory, and this is reflected in the completion candidates used by ‘lacarte-execute-menu-command’.


2006-03-15, SteveBerman posted some code to that takes recentf filters into account in another way. Here is Steve’s commentary, followed by his code. This requires enabling recursive minibuffer editing: (setq enable-recursive-minibuffers t). Steve: Why don’t you upload this as an Emacs-Lisp library? – DrewAdams

One of the features of recentf.el that I find most useful is the possibility of filtering ‘recentf-list’ and structuring the recent file menu in accordance with the filter. So I wrote some code that makes the filtering and structuring of the recent file menu accessible from the minibuffer, with standard tabbed completion. Here it is. (I find it convenient to globally bind ‘C-c r’ to the command ‘recentf-minibuffer-dialog’.) – SteveBerman
(defun recentf-filtered-list (arg)
  "Return a filtered list of ARG recentf items."
     (mapcar 'recentf-make-default-menu-element
	     (butlast recentf-list (- (length recentf-list) arg)))))

(defun recentf-list-submenus (arg)
  "Return a list of the recentf submenu names."
  (if (listp (cdar (recentf-filtered-list arg))) ; submenues exist
      (delq nil (mapcar 'car (recentf-filtered-list arg)))))

(defmacro recentf-list-entries (fn arg)
  "Return a list of ARG recentf menu entries as determined by FN.
When FN is `'car' return the menu entry names, when FN is `'cdr'
return the absolute file names."
  `(mapcar (lambda (x) (mapcar ,fn x))
	   (if (recentf-list-submenus ,arg)
	       (mapcar 'cdr (recentf-filtered-list ,arg))
	     (list (recentf-filtered-list ,arg)))))

;; This function is not specific to recentf mode but is needed by
;; `recentf-minibuffer-dialog'.  I've also made enough use of it in
;; other contexts that I'm surprised it's not part of Emacs, and the
;; fact that it isn't makes me wonder if there's a preferred way of
;; doing what I use this function for.
(defun recentf-memindex (mem l)
  "Return the index of MEM in list L."
  (let ((mempos -1) ret)
    (while (eq ret nil)
      (setq mempos (1+ mempos))
      (when (equal (car l) mem) (setq ret mempos))
      (setq l (cdr l)))

(defun recentf-minibuffer-dialog (arg)
  "Open the recentf menu via the minubuffer, with completion.
With positive prefix ARG, show the ARG most recent items.
Otherwise, show the default maximum number of recent items."
  (interactive "P")
  (let* ((num (prog1 (if (and (not (null arg))
			      (> arg 0))
			 (min arg (length recentf-list))
		(and (not (null arg))
		     (> arg (length recentf-list))
		     (message "There are only %d recent items."
			      (length recentf-list))
		     (sit-for 2))))
	 (menu (if (recentf-list-submenus num)
		   (completing-read "Open recent: "
				    (recentf-list-submenus num))))
	 (i (recentf-memindex menu (recentf-list-submenus num)))
	 (items (nth i (recentf-list-entries 'car num)))
	 (files (nth i (recentf-list-entries 'cdr num)))
	 (item (completing-read "Open recent: " items))
	 (j (recentf-memindex item items))
	 (file (nth j files)))
    (funcall recentf-menu-action file))) ; find-file by default

Icicles Recentf Support

Icicles lets you take advantage of library recentf.el in two ways.

Multi-Command `icicle-recent-file'

‘recentf-mode’ of vanilla Emacs forces you to open a file using a menu interface. Icicles command ‘icicle-recent-file’ lets you use the usual ‘find-file’ minibuffer interface, with completion and cycling among your recent files.

‘icicle-recent-file’ is also a multi-command, so you can use it to open several recently used files at once. And the completion candidates are two-part multi-completions: (1) the (absolute) file name and (2) the file’s last modification date. You can match your minibuffer input against either or both parts. This means that you can easily find those notes you took sometime last week…

Including Recent Files for Buffer Completion

In Icicle mode, ‘C-x b’ (command ‘icicle-buffer’) can optionally include the names of recently accessed files as completion candidates (there need not currently be any buffers for the files). The [option] for this is `icicle-buffer-include-recent-files-nflag’. You can toggle the option value during buffer-name completion using ‘C-x R’ (command ‘icicle-toggle-include-recent-files’). A prefix argument sets the option value to the numeric prefix value.

The option value is a non-zero integer. If positive then recent file names are included; if negative they are not. The absolute value is the maximum number of recent-file candidates to include when turned on. So for example, if the option value is 20 then the names of only the twenty most recently visited files are candidates.

ido (iswitchb ?) Completion

Here is another version of recent-files completion, which use InteractivelyDoThings (ido). Not perfect, but seems to work ok. You first choose among filenames and if more than one filename matches, you are prompted to choose again among the matching files, but the path is added to the list.

It should work (untested – see UseIswitchBuffer) using the IswitchBuffers package instead, by replacing:

(defun recentf-interactive-complete ()
  "find a file in the recently open file using ido for completion"
  (let* ((all-files recentf-list)
	 (file-assoc-list (mapcar (lambda (x) (cons (file-name-nondirectory x) x)) all-files))
	 (filename-list (remove-duplicates (mapcar 'car file-assoc-list) :test 'string=))
	  (lambda ()
	    (setq ido-temp-list filename-list)))
	 (filename (ido-read-buffer "Find Recent File: "))
	 (result-list (delq nil (mapcar (lambda (x) (if (string= (car x) filename) (cdr x))) file-assoc-list)))
	 (result-length (length result-list)))
	   ((= result-length 0) filename)
	   ((= result-length 1) (car result-list))
	   ( t
	     (let ( (ido-make-buffer-list-hook
		     (lambda ()
		       (setq ido-temp-list result-list))))
	       (ido-read-buffer (format "%d matches:" result-length))))

Or this somewhat simpler version:

(defun recentf-ido-find-file ()
  "Find a recent file using Ido."
  (let ((file (ido-completing-read "Choose recent file: " recentf-list nil t)))
    (when file
      (find-file file))))

where file-name-nondirectory could be applied to recentf-list if preferred.

And this is a version with file-name-nondirectory.

(defun recentf-ido-find-file ()
  "Find a recent file using Ido."
  (let* ((file-assoc-list
	  (mapcar (lambda (x)
		    (cons (file-name-nondirectory x)
	  (remove-duplicates (mapcar #'car file-assoc-list)
			     :test #'string=))
	 (filename (ido-completing-read "Choose recent file: "
    (when filename
      (find-file (cdr (assoc filename


This command also provides completion for your recent files. It was written by Jerome B. and first posted in an article for

    (defun recentf-open-files-compl ()
      (let* ((all-files recentf-list)
        (tocpl (mapcar (function 
           (lambda (x) (cons (file-name-nondirectory x) x))) all-files))
        (prompt (append '("File name: ") tocpl))
        (fname (completing-read (car prompt) (cdr prompt) nil nil)))
        (find-file (cdr (assoc-ignore-representation fname tocpl))))) 

Jerome binds it to C-x C-r. (He rarely uses ‘find-file-read-only’.)

    (global-set-key "\C-x\C-r" 'recentf-open-files-compl)

Jerome uses this function more often than ‘find-file’.

This function does not work on XEmacs 21.4.14 nor does it work with XEmacs/TrampMode, but it does with the following modifications:

    (defun recentf-open-files-compl ()
      (let* ((all-files recentf-list)
    	 (tocpl (mapcar (function 
    	(lambda (x) (cons (file-name-nondirectory x) x))) all-files))
    	 (prompt (append '("Recent File name: ") tocpl))
    	 (fname (completing-read (car prompt) (cdr prompt) nil nil)))
        (find-file (or (cdr (assoc fname tocpl))

On modern Emacs, assoc-ignore-representation is deprecated and the lisp style of the functions above is somewhat odd. Here’s a tidier version of the same (excellent) idea:

    (defun recentf-open-files-compl ()
      (let* ((tocpl (mapcar (lambda (x) (cons (file-name-nondirectory x) x))
             (fname (completing-read "File name: " tocpl nil nil)))
        (when fname
          (find-file (cdr (assoc-string fname tocpl)))))) 

Using iswitch(b)

See IswitchRecentFiles for using recent files with IswitchB.

Use with Helm(Anything)

Anything is a candidate selection framework, now called Helm.

‘M-x helm-for-files’ will show a list of open buffers, recent files, and other filenames. Narrow the list by typing some patterns (multiple patterns are space-delimited strings); select with up/down/pgup/pgdn/C-p/C-n/C-v/M-v, choose with enter. With TAB actions can be selected if the selected files has more than one possible action. With C-z content of files is displayed without quitting the helm session.

‘M-x helm-recentf’ will show only recently opened files (from recentf).


When using TrampMode with recentf.el, it’s advisable to turn off the cleanup feature of recentf that attempts to stat all the files and remove them from the recently accessed list if they are readable. Tramp means that this requires recentf to open up a remote site which will block your emacs process at the most inopportune times.

(require 'recentf)
(setq recentf-auto-cleanup 'never) ;; disable before we start recentf!
(recentf-mode 1)


I use Gnus in one process, and everything related to plain file editing in another emacs process. My initial load starts recentf-mode but for gnus, I really needed the recentf to use a different history file.

;; use a different set of recent files for gnus
(setq recentf-save-file (recentf-expand-file-name "~/.recentf.gnus"))

;; prevent double initialization of recentf
(if (not (boundp 'cmg:prevent-gnus-recentf-double-init))
    (setq cmg:prevent-gnus-recentf-double-init nil))

(if (not cmg:prevent-gnus-recentf-double-init)
      (load-file recentf-save-file)
      (setq cmg:prevent-gnus-recentf-double-init t)))


picklist was once forked from recent-files when the latter didn’t store positions in the file and has evolved since then. It introduced merging of lists and a regexp based apropos.

Please note, that it vanished from the internet but if someone wants the code I can still send it via Email – StefanKamphausen

Buffer with the list of recently open files.

Lisp:recentf-buffer.el – creates a buffer with the list of recently open files. It’s an addition to recentf.el. To install:

(load "recentf-buffer")
(global-set-key [?\C-c ?r ?f] 'recentf-open-files-in-simply-buffer)


Icicles gives you the list when you use ‘icicle-recent-file’ or ‘icicle-recent-file-other-window’ (see RecentFilesWithCompletion above). Just hit ‘TAB’ or ‘S-TAB’. If you type a regexp, ‘S-TAB’ will give you the list of matching recent files. – DrewAdams

Library `recentf-ext.el'

Lisp:recentf-ext.el extends recentf package.


Visited Files Sorted by Access Date/Time

This concerns buffers visiting files, so it is related to some of the topics on this page. But this does not concern a persistent visited-files list.

See BufferMenuPlus for a description of the functionality of library Lisp:buff-menu+.el. With this library, you can sort visited files by date/time in two different ways (each either ascending or descending):

This is especially useful if you have a large number of visited files, and you want to choose one of the recently used file buffers (time of last access) or one of the recently opened files (time of first visit, reversed). Reminder: You can use ‘T’ (command ‘Buffer-menu-toggle-files-only’) to display only buffers visiting files.

Note that these two kinds of sort are not inverses. Example: if you opened a file yesterday that you are still visiting, and all other currently visited files were opened today, then that file is at the top (or bottom) when you sort by column CRM. When you sort by column Time, its position depends on the last time it was accessed. – DrewAdams

Merge Lists Of Several Emacsen

I wrote to the maintainer of recentf.el but received no feedback til now, so I might as well share my code here.

I have the habit to start one Emacs dedicated to Gnus first thing in the morning and close it last thing in the evening. With the standard recentf storage habit this Emacs will always overwrite the list of recent files, thus making it more or less constant. There are several ways to solve this, one would be to read the list before saving it and merging the list from the file with that in the current instance of Emacs before saving it back. My code for this, which I have in daily usage for a while now, is listed below. Somehow I can’t just overwrite the functions from recentf but I need to find the right time for that, that’s why I put it after the requiring and befor the activation. – StefanKamphausen

(defun try-require (feature)
  (condition-case nil
      (require feature)
    (error (progn
             (message "could not require %s" feature)

(when (try-require 'recentf)
  (setq recentf-exclude '("~$"))
  (setq recentf-max-saved-items 99)
  ;; Here I override some functions of recentf to get a merged list
  ;; This seems to be stable (used it for approx two weeks at the time 
  ;; of this writing)
  (defun recentf-save-list ()
    "Save the recent list.
Load the list from the file specified by `recentf-save-file',
merge the changes of your current session, and save it back to the
    (let ((instance-list (copy-list recentf-list)))
      (recentf-merge-with-default-list instance-list)

  (defun recentf-merge-with-default-list (other-list)
    "Add all items from `other-list' to `recentf-list'."
    (dolist (oitem other-list)
      ;; add-to-list already checks for equal'ity
      (add-to-list 'recentf-list oitem)))

  (defun recentf-write-list-to-file ()
    "Write the recent files list to file.
Uses `recentf-list' as the list and `recentf-save-file' as the
file to write to."
    (condition-case error
          (set-buffer-file-coding-system recentf-save-file-coding-system)
          (insert (format recentf-save-file-header (current-time-string)))
          (recentf-dump-variable 'recentf-list recentf-max-saved-items)
          (recentf-dump-variable 'recentf-filter-changer-current)
          (insert "\n \n;;; Local Variables:\n"
                  (format ";;; coding: %s\n" recentf-save-file-coding-system)
                  ";;; End:\n")
          (write-file (expand-file-name recentf-save-file))
          (when recentf-save-file-modes
            (set-file-modes recentf-save-file recentf-save-file-modes))
       (warn "recentf mode: %s" (error-message-string error)))))
  (recentf-mode 1))

I wrote an alternative implementation that uses dash.el and supports recentf-cleanups – ThomasFrössman

    (defvar recentfs-list-on-last-sync nil
      "List of recent files reference point.")

    (defun recentfs-update-sync ()
      "Load saved projects from `recentf-list'."
      (setq recentfs-list-on-last-sync
            (and (sequencep recentf-list)
               (copy-sequence recentf-list))))

    (defadvice recentf-load-list (after recentfs-loaded-sync activate)

    (defadvice recentf-save-list (around recentfs activate)

    (defun recentfs-load-list ()
      "Load a previously saved recent list and return it as a value
instead of setting it."
      (let ((file (expand-file-name recentf-save-file))
            (recentf-filter-changer-current nil) ;; ignored atm
            (recentf-list nil))
        (when (file-readable-p file)
          (load-file file))

    (defun recentfs-merge-lists ()
      "Merge any change from `recentf-list'.

This enables multiple Emacs processes to make changes without
overwriting each other's changes."
      (let* ((known-now recentf-list)
             (known-on-last-sync recentfs-list-on-last-sync)
             (known-on-file (recentfs-load-list))
             (removed-after-sync (-difference known-on-last-sync known-now))
              (-difference known-on-last-sync known-on-file))
               (-concat removed-after-sync removed-in-other-process known-now)))
             (result (-distinct
                       (-concat new-in-other-process known-now)
                       (-concat removed-after-sync removed-in-other-process)))))
        (setq recentf-list result)))

Entries for files that were never displayed

If you, for example, use CEDET, the recentf package may be fairly useless by default. The problem is that CEDET can scan lots of source files and make files in the process of building a tags database or managing the build system. The recentf list is then saturated with files you haven’t displayed or edited on screen.

This is fixed in the CVS version of CEDET.

The following is a workaround.

(require 'recentf)
(recentf-mode 1)

(defsubst file-was-visible-p (file)
  "Return non-nil if FILE's buffer exists and has been displayed."
  (let ((buf (find-buffer-visiting file)))
    (if buf
      (let ((display-count (buffer-local-value 'buffer-display-count buf)))
        (if (> display-count 0) display-count nil)))))

(defsubst keep-default-and-visible-recentf-p (file)
  "Return non-nil if recentf would, by default, keep FILE, and
FILE has been displayed."
  (if (recentf-keep-default-predicate file)
      (file-was-visible-p file)))

;; When a buffer is closed, remove the associated file from the recentf
;; list if (1) recentf would have, by default, removed the file, or
;; (2) the buffer was never displayed.  This is useful because, for
;; example, CEDET opens a lot of files in the background to generate
;; its tags database, etc.
(setq recentf-keep '(keep-default-and-visible-recentf-p))

While this works and solves problems with several packages that load files and do not need to be displayed, it has one drawback, namely that files saved to the recentf list in previous sessions will be deleted from the list if they have not been displayed in the current session before the list is cleaned up. This is probably not what you want. The following code gets round this:

(require 'recentf)
(recentf-mode 1)

(defsubst file-was-visible-p (file)
  ;; as before
  ;; ---

(let ((r-list recentf-list))
  (defsubst keep-default-old-and-visible-recentf-p (file)
    "Decide whether to keep file in recentf-list.
Return non-nil if recentf would, by default, keep FILE, and
either FILE name was loaded from recentf file on disk or FILE
has been displayed in this session."
    (if (recentf-keep-default-predicate file)
      (or (member file r-list)
      (file-was-visible-p file)))))

;; And, of course, you now need:
(setf recentf-keep '(keep-default-old-and-visible-recentf-p))		    

Note that the above code assumes lexical binding. If you don’t use this you will have to define a new global variable to hold the old recentf list.

Undo kill buffer

The Opera web browser has a surprisingly useful feature called the “trash can”. After you close a tab you can “undo” the close with C-z. As it turns out when I want a tab reopened it’s usually one of the last few I closed, so I almost never end up looking in the history. I missed this feature in Emacs, so I implemented it on top of recentf.

(defun undo-kill-buffer (arg)
  "Re-open the last buffer killed.  With ARG, re-open the nth buffer."
  (interactive "p")
  (let ((recently-killed-list (copy-sequence recentf-list))
	  (delq nil (mapcar (lambda (buf)
			      (when (buffer-file-name buf)
				(expand-file-name (buffer-file-name buf)))) (buffer-list)))))
     (lambda (buf-file)
       (setq recently-killed-list
	     (delq buf-file recently-killed-list)))
     (if arg (nth arg recently-killed-list)
       (car recently-killed-list)))))

See Also: GnomeIntegration for opening files in GNOME’s recent-files list.

recentf and dired buffers

After evaluating the following code the directories visited through dired buffers will also be put to recentf. – vibrys.

(defun recentd-track-opened-file ()
  "Insert the name of the directory just opened into the recent list."
  (and (derived-mode-p 'dired-mode) default-directory
       (recentf-add-file default-directory))
  ;; Must return nil because it is run from `write-file-functions'.

(defun recentd-track-closed-file ()
  "Update the recent list when a dired buffer is killed.
That is, remove a non kept dired from the recent list."
  (and (derived-mode-p 'dired-mode) default-directory
       (recentf-remove-if-non-kept default-directory)))

(add-hook 'dired-after-readin-hook 'recentd-track-opened-file)
(add-hook 'kill-buffer-hook 'recentd-track-closed-file)

Alternative solution is to redefine recentf-track-opened-file and recentf-track-closed-file core emacs functions. This solution is twice as fast (does it matter?), but the drawback is that emacs functions are redefined.

For emacs 24.5.1 do the following:

(eval-after-load "recentf"
(defun recentf-track-opened-file ()
  "Insert the name of the dired or file just opened or written into the recent list."
  (let ((buff-name (or buffer-file-name (and (derived-mode-p 'dired-mode) default-directory))))
    (and buff-name
         (recentf-add-file buff-name)))
  ;; Must return nil because it is run from `write-file-functions'.

(defun recentf-track-closed-file ()
  "Update the recent list when a file or dired buffer is killed.
That is, remove a non kept file from the recent list."
  (let ((buff-name (or buffer-file-name (and (derived-mode-p 'dired-mode) default-directory))))
    (and buff-name
         (recentf-remove-if-non-kept buff-name))))

(add-hook 'dired-after-readin-hook 'recentf-track-opened-file)))

Tip: when helm is used, use “someDir/$” to narrow to directory only pattern. “someDir” pattern also works of course, but it will display other paths containing “someDir” as well. – vibrys

Updating recentf-list when renaming files in dired

If you move a source directory with dired from A to B, by default there’s no integration to make sure that an entry of A/xxx gets updated to B/xxx. This snippet of code makes this work properly (it uses the new Emacs advice functions; if you have an Emacs older than 24.4, you’ll have to port it).

;; Magic advice to rename entries in recentf when moving files in
;; dired.
(defun rjs/recentf-rename-notify (oldname newname &rest args)
  (if (file-directory-p newname)
      (rjs/recentf-rename-directory oldname newname)
    (rjs/recentf-rename-file oldname newname)))

(defun rjs/recentf-rename-file (oldname newname)
  (setq recentf-list
        (mapcar (lambda (name)
                  (if (string-equal name oldname)

(defun rjs/recentf-rename-directory (oldname newname)
  ;; oldname, newname and all entries of recentf-list should already
  ;; be absolute and normalised so I think this can just test whether
  ;; oldname is a prefix of the element.
  (setq recentf-list
        (mapcar (lambda (name)
                  (if (string-prefix-p oldname name)
                      (concat newname (substring name (length oldname)))

(advice-add 'dired-rename-file :after #'rjs/recentf-rename-notify)


An older version is available here:

CategoryCommands CategoryDotEmacs CategoryPersistence CategoryCompletion