Library / SDK
tumashu/posframe avatar
tumashu/posframe

posframe: popping an Emacs child frame at point for a quick note

Pop a posframe (just a child-frame) at point, posframe is a **GNU ELPA** package!

539 stars66 forksEmacs LispLicense varies

At a glance

What is it?
posframe is a small Emacs Lisp package that opens a child frame attached to the current buffer at point, fast enough for daily use and tested with CJK input, distributed through GNU ELPA, GNU-devel ELPA and MELPA. It suits an Emacs user who wants a scratch popup without leaving the buffer, and it is one file of code with a small argument list.
Who is it for?
posframe is worth adding to an Emacs configuration if you keep a scratch note next to the code you are reading, because the frame is a child of the current buffer so it moves and resizes with it, and because the implementation is small enough to read in a sitting and to adapt. It is the wrong tool if you want a second window you can put somewhere else, and it is the wrong tool if your Emacs is too old for the frame behaviour it relies on.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Emacs Lisp, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the package does, and the two claims it makes

posframe pops up a frame at point, and the readme is precise about what kind of frame it is: this posframe is a child-frame connected to its root window's buffer.

That single architectural detail is the whole design. A child frame in the Emacs windowing system is not an independent window that happens to appear somewhere on the screen. It is a frame that belongs to a window, and when the parent window moves or resizes, the child goes with it. A note that appears next to the code you are reading stays next to it, and it does not become an orphan window that you then have to close.

The two stated advantages are speed and CJK support. The package is fast enough for daily usage, which is the point of the design, because a popup that takes a noticeable fraction of a second to open gets closed rather than used, and the workarounds for East Asian text in Emacs are well known to be slow. A package that claims to work well with CJK languages is claiming to have dealt with that.

The package is a GNU ELPA package, and the badges in the readme show it on GNU ELPA, on the GNU-devel ELPA and on MELPA, which is the normal three-way distribution for an Emacs package that wants to be installable by the major package systems.

The readme is in the Org format, with a created line dated 2021-06-01 and the author given as Feng Shu, and the repository holds very little: a posframe.el file, a posframe-benchmark.el file, a snapshots/ directory with an image, and the readme itself. The benchmark file is a good sign for a package whose main claim is speed, because the claim is measured rather than asserted.

The primary language is Emacs Lisp, the default branch is master, and the last push to the repository was on 2026-10-19, so the package is being maintained even though the readme itself has not been rewritten in years.

The two ways to call it, and why the difference matters

The usage section documents a simple path and an advanced path, and the difference between them is about ownership of the content.

The simple way passes a string and a position:

elisp
(when (posframe-workable-p)
  (posframe-show " *my-posframe-buffer*"
                 :string "This is a test"
                 :position (point)))

The advanced way creates the buffer first and hands it over:

elisp
(defvar my-posframe-buffer " *my-posframe-buffer*")

(with-current-buffer (get-buffer-create my-posframe-buffer)
  (erase-buffer)
  (insert "Hello world"))

(when (posframe-workable-p)
  (posframe-show my-posframe-buffer
                 :position (point)))

The advanced form is what you want the moment the content is anything other than a fixed string, because it gives you a real buffer you can insert into, kill, and keep a reference to. The leading space in the buffer name, the *my-posframe-buffer* convention, is the standard Emacs way of marking a buffer as internal, and it is what keeps the frame's buffer out of the buffer list and out of the way of your normal workflow.

The guard around both calls is worth noticing too. The calls are wrapped in a when form testing posframe-workable-p, so the code asks the package whether a frame can be shown on the current display before attempting it, which is the difference between a package that degrades quietly on an unusual setup and one that throws.

And the documentation does not restate the argument list. Instead of duplicating the signature in the readme, it points at the function help with C-h f posframe-show, which is the right call for an Emacs package, because the authoritative argument list lives with the code and a second copy in a readme is a copy that goes stale.

Hiding and deleting, and the one command that warns you

The rest of the interface is the housekeeping you would expect, and the documentation is candid about the sharp edge in it.

Hiding one posframe is a single call, and hiding all of them is an interactive command:

elisp
(posframe-hide " *my-posframe-buffer*")

with the command-line form given as M-x posframe-hide-all.

Deleting is split into two operations, which is a deliberate distinction. Deleting a posframe and its buffer is one call, and deleting only the frame is another:

elisp
(posframe-delete " *my-posframe-buffer*")
elisp
(posframe-delete-frame " *my-posframe-buffer*")

Deleting all of them is again an interactive command, M-x posframe-delete-all, and it comes with the note that this command will delete all posframe buffers, and that you probably shouldn't use it if you are sharing a buffer between posframe and other packages.

That warning is the kind of detail that distinguishes a package written by someone who has been burned. The delete-all command destroys the buffers, not just the windows, and a buffer that some other package is also using is a buffer that another package will later try to display. Since the advanced usage pattern explicitly involves creating a buffer you own and inserting into it, that buffer is exactly the kind of thing somebody else might be sharing.

The rest of the interface history is also recorded. The posframe-arghandler feature has been removed from posframe-1.1, and the user can use the advice feature instead. That is a small breaking change noted in the readme, and the fact that it is still there from the 1.1 release is a sign the author keeps the changelog in the readme rather than only in commit messages.

Installing it, and the benchmark file that came with it

Installation is the usual Emacs package procedure, and the readme states it in one line:

elisp
(require 'posframe)

That is the whole instruction, and it is worth contrasting with the amount of configuration some Emacs packages need. The reason is the package archives: because posframe is published on GNU ELPA, on the GNU-devel ELPA and on MELPA, the package manager handles the download, the version and the byte compilation, and the require line is all a user has to write.

The archives are also a maintenance signal. A package on the GNU-devel ELPA is one where the author considers the development version interesting enough to publish, which is a stronger commitment than having a tag on a repository, and MELPA is the largest general Emacs package archive, so a package on all three has opted into the ordinary distribution path rather than asking users to fetch a file.

The second file in the repository is the interesting one for anyone evaluating the speed claim. Alongside posframe.el there is a posframe-benchmark.el, and a package whose central promise is that it is fast enough for daily use has shipped the measurement harness alongside it.

That is a small thing that says a lot about how the project is run. The readme claims speed as one of two main advantages, and rather than asking the reader to take the claim on faith, the repository contains the benchmark that produced it. The same is true of the historical note: the removal of the posframe-arghandler feature in version 1.1 is written into the readme with the alternative, the advice feature, named, so a user upgrading finds the change and its replacement in the document rather than in a commit message.

So the repository is small, a single elisp file plus a benchmark, and the distribution and the documentation are both carried by the standard Emacs mechanisms. For a package this focused, that is the right size.

The platform constraints, and the mouse behaviour that needs a setting

The readme lists two version constraints and one configuration, and each of them corresponds to a specific interaction between Emacs frames and the window manager.

For macOS users, posframe needs Emacs version 26.0.91 or later. For GNOME users with GTK3 builds, it needs Emacs 27 or later, and the documentation points at the variable posframe-gtk-resize-child-frames, which auto-detects that configuration.

The linked commits and mailing list messages explain the why, and they are the same story told from two ends. The first is a change in Emacs 27 titled Fix some problems with moving and resizing child frames, and the second is a message on the Emacs development list noting that Emacs's set-frame-size can not work well with gnome-shell.

The mechanism is worth understanding. A child frame that is resized programmatically asks the window manager to resize it, and under gnome-shell the window manager may not honour that request, so the frame ends up the wrong size or in the wrong place. Emacs 27 changed how child frames are moved and resized to make this work, which is why the version floor is what it is.

The mouse behaviour is the last configuration item. The default setting will work well in most cases, but for EXWM users, which is the tiling window manager popular with Emacs power users, the readme suggests a specific setting:

elisp
(setq posframe-mouse-banish-function #'posframe-mouse-banish-simple)

The feature in question is mouse banish, the trick where the pointer is pushed off the edge of the screen when the mouse is moved quickly, so it does not sit over the text you are reading. Under a tiling window manager the pointer cannot be banished that way, and the alternative function is the one that works with EXWM. It is a one-line setting for a behaviour that would otherwise silently be wrong, and the readme tells you exactly which users need it.

Editorial conclusion

posframe is worth adding to an Emacs configuration if you keep a scratch note next to the code you are reading, because the frame is a child of the current buffer so it moves and resizes with it, and because the implementation is small enough to read in a sitting and to adapt. It is the wrong tool if you want a second window you can put somewhere else, and it is the wrong tool if your Emacs is too old for the frame behaviour it relies on. Before you install it, check three things: that your Emacs is new enough, since macOS users need version 26.0.91 or later and GNOME users with GTK3 builds need Emacs 27 or later because of how child frames resize under gnome-shell, with the posframe-gtk-resize-child-frames variable auto-detecting that configuration; that the package came from a package archive rather than a file you copied, since it is published on GNU ELPA, on the GNU-devel ELPA and on MELPA, and the archive is what carries the version history; and that you read the argument list through the function help, because the simple and advanced ways of calling it differ in whether the frame content is a string or a buffer you own, and the documentation points at C-h f posframe-show rather than reproducing the signature.

Frequently asked questions

What is posframe and what does it do?

It pops up a frame at point, and that posframe is a child-frame connected to its root window's buffer, which means it moves and resizes with the window it belongs to. The package is created and maintained by Feng Shu, is published as a GNU ELPA package on GNU ELPA, the GNU-devel ELPA and MELPA, and is fast enough for daily use and works well with CJK languages.

How do I show and hide a posframe in Emacs?

Show one with posframe-show, passing a buffer name, a :string and a :position, or create the buffer yourself and pass it, and guard the call with posframe-workable-p. Hide one with posframe-hide, hide all with M-x posframe-hide-all, and delete with posframe-delete or posframe-delete-frame, or M-x posframe-delete-all, which also deletes the buffers and is not safe when a buffer is shared with another package.

Which Emacs versions does posframe need?

macOS users need Emacs 26.0.91 or later, and GNOME users with GTK3 builds need Emacs 27 or later, because of how child frames are moved and resized under gnome-shell. The variable posframe-gtk-resize-child-frames auto-detects that configuration, and EXWM users are advised to set posframe-mouse-banish-function to posframe-mouse-banish-simple.

Official sources

  1. Issues
  2. README
  3. tumashu/posframe on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tumashu-posframe.svg)](https://hysenlabs.com/projects/tumashu-posframe)

Was this useful?