# skewer-mode evaluates JavaScript in a browser from inside Emacs

> Skewer is a pure Elisp package that gives Emacs a live connection to a browser: expressions from an editing buffer are sent on the fly and evaluated in the page, the way an inferior Lisp process is used in a Lisp mode. It works with CSS and HTML as well as JavaScript, attaches several browsers at once, and ships under the Unlicense.

**skeeto/skewer-mode** — Live web development in Emacs

- Repository: https://github.com/skeeto/skewer-mode
- Stars: 1,127 · Forks: 57
- Language: Emacs Lisp
- License: Unlicense
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/skeeto-skewer-mode

## Emacs skewering the browser from the server side

Skewer provides live interaction with JavaScript, CSS and HTML in a web browser. Expressions are sent on the fly from an editing buffer to be evaluated in the browser, just like Emacs does with an inferior Lisp process in Lisp modes.

That analogy is the whole design, and it explains both the shape of the interface and the limits. The browser becomes the inferior process. You send it a form, it runs it, and the value comes back into the minibuffer. The package is written in Emacs Lisp, the primary language of the repository, and it is distributed under the Unlicense, which is as close to public domain as a licence gets.

The project also states the motivation, and it is worth reading because it sets the bar. The author wanted something like swank-js but without all the painful setup, and had already written an Emacs web server, so it took relatively little code to accomplish. There was also a deliberate avoidance of browser specific features such as MozRepl or the WebKit remote debugger, the kite project, and the reason is portability: if the mechanism is a script served over HTTP and evaluated by the page, it works in any browser that can run JavaScript.

The name comes from the idea that Emacs is skewering the browser from the server side, and it is a good name for a tool whose job is to reach out of the editor and touch a page.

The audience is a web developer who lives in Emacs and wants the iteration loop of a console without a browser tab in the way, and who is willing to work in a model where the browser is a remote Lisp.

## MELPA gets you the dependencies, a checkout needs two

Skewer is available from MELPA, which will install the dependencies for you, and the readme stresses the practical benefit: this package and its dependencies are pure Elisp, meaning setup is a breeze, the whole thing is highly portable, and it works with many browsers.

The dependencies are two. simple-httpd, the Emacs web server, which is available on MELPA, and js2-mode, the JavaScript major mode, which is available on ELPA. Skewer requires Emacs 24.3 or later.

If you did not install from MELPA, the readme gives three steps. Put the repository directory in your load-path, load skewer-mode.el, and then run M-x run-skewer to attach a browser to Emacs. From a js2-mode buffer with the skewer-mode minor mode enabled, you can send forms to the browser to evaluate.

There is a difference of emphasis between that and the next step, and it matters. run-skewer is a convenience, and the readme notes that it uses browse-url to launch the browser, which may require further setup depending on your operating system and personal preferences. The alternative is to do the wiring yourself, and the readme spells out the manual sequence: load the dependencies, load skewer-mode.el, start the HTTP server with httpd-start, include http://localhost:8080/skewer as a script in the document, and then visit the document from your browser.

The manual route is more work and it is the more interesting one, because it is the route that lets you skewer a page you did not author, which is what the next sections are about.

## The keybindings are the Lisp mode keybindings

The bindings are the part that makes this feel native, because they are not new keys, they are the same keys you already use in a Lisp buffer.

C-x C-e evaluates the form before the point and displays the result in the minibuffer, and if given a prefix argument it inserts the result into the current buffer instead. C-M-x evaluates the top-level form around the point. C-c C-k loads the current buffer, and C-c C-z selects the REPL buffer. The result of the expression is echoed in the minibuffer.

CSS and HTML get similar sets of bindings for modifying the rules and the markup on the current page. In CSS mode, C-x C-e loads the declaration at the point, C-M-x loads the entire rule around the point, and C-c C-k loads the current buffer as a stylesheet. In HTML mode, C-M-x loads the HTML tag immediately around the point.

The minor modes are enabled through hooks, and the readme is precise about the current state of that. The function skewer-setup can be used to configure all of the mode hooks, and the documentation notes that previously this was the default, so a configuration that calls skewer-setup itself is the modern form. The manual equivalent is three lines:

```el
(add-hook 'js2-mode-hook 'skewer-mode)
(add-hook 'css-mode-hook 'skewer-css-mode)
(add-hook 'html-mode-hook 'skewer-html-mode)
```

So the price of the convenience is that you now have to call the setup function instead of getting it by default, which is a small thing and one that a security conscious Emacs user would probably want anyway.

## Several browsers at once, and a long poll that expires

Two behaviours of the transport are worth knowing about, because both will surprise you the first time they happen.

The first is that multiple browsers and browser tabs can be attached to Emacs at once. JavaScript forms are sent to all attached clients simultaneously, and each will echo back the result individually. The command list-skewer-clients shows the list of all currently attached clients. The practical effect is that a form that mutates the page will run in every attached tab, and the minibuffer will show several results, which is exactly what you want when you are looking at the same page in two window sizes and not what you want when you have three unrelated tabs open.

The second is the timeout. Sometimes Skewer's long polls from the browser will time out after a number of hours of inactivity, and if you find the browser disconnected from Emacs for any reason, the fix is to call skewer() in the browser console to reconnect. The readme explains why this matters: it avoids a page reload, which would lose any fragile browser state you might care about. That is a real consideration for a tool used on a page with a session, a form half filled in, or a debugger attached.

So there are two states to distinguish. A browser that is attached and idle is fine, it will time out and reconnect on demand. A browser that is attached and evaluating is sending results back, so the page state is live. When you go back to the browser after a long break, call skewer() rather than reloading, and you keep whatever the page was holding.

## CORS, the userscript and the bookmarklet

Skewer supports Cross-origin Resource Sharing, which means you can skewer a document hosted from any server without needing special changes on that server, except for including /skewer as a script in that document.

There is an important warning attached to the script itself. The readme says not to copy skewer.js anywhere or use it directly, because Emacs hosts this script itself, manipulating it in memory before it reaches the browser, and you should always access it through the servlet on the Emacs webserver as /skewer. That is a design detail that has a practical consequence: the script is generated per session, and a copied copy will not behave.

For the case where you do not control the server, there is a Greasemonkey userscript, Skewer Everything, for injecting Skewer into any page you are visiting without modifying the page on the host. The userscript assumes the Skewer server is running at http://localhost:8080/, the simple-httpd default port, and you need to edit the top of the userscript if that is not true. The script is not actually injected until you switch the toggle in the top-right corner, the red and green triangle.

There is also a bookmarklet that loads Skewer on demand:

```js
javascript:(function(){var d=document;var s=d.createElement('script');s.src='http://localhost:8080/skewer';d.body.appendChild(s);})()
```

And with a browser extension such as Custom Javascript for Websites, you can use the bookmarklet to auto-skew specific domains, saving a click on each reload.

Browser support is listed as Firefox, Chrome, Safari, Opera and IE8 and later. Except for CSS and HTML, Skewer will work in IE7 when document.querySelector and JSON are polyfilled, and the readme asks you to report any other JavaScript capable browser that does not work.

## The REPL is a console inside the browser

A REPL into the browser can be created with M-x skewer-repl, or with C-c C-z. It should work like a console within the browser, and messages can be logged to it with skewer.log(), the equivalent of console.log().

The readme notes one difference from the minibuffer path. Results of expressions evaluated in the REPL are printed more verbosely than in the minibuffer, when possible, and the documentation says this may help in debugging. That is a small thing and it points at the intended use, which is a session where you are poking at a page and reading structured output rather than evaluating one expression and moving on.

There is one more feature in the package that deserves an honest mention rather than a recommendation. There are also some functions for loading libraries from the bower infrastructure on the fly, accessed with skewer-bower-load, and the author says he often finds it useful to load jQuery when skewering a page that does not have jQuery installed.

The readme then warns that bower does not need to be installed, only git, because the bower infrastructure is being used, and that the infrastructure is currently a mess. Many packages are in some sort of broken state, with missing dependencies, missing metadata, broken metadata, or an invalid repository URL, and the readme attributes some of this to under-specification of the metadata by the bower project.

So the loader works, the ecosystem it loads from does not, and if you take one thing from this section it is that the useful part of Skewer is the live evaluation, not the library loader.

## The Unlicense, a Makefile, and no tagged releases

The licence is the Unlicense, with the UNLICENSE file at the root of the repository. That is a dedication to the public domain, and for a tool of this size and this age it is a reasonable choice.

The repository is a flat set of Elisp files. skewer-mode.el is the core, skewer-setup.el holds the setup function, skewer-repl.el the REPL, skewer-css.el and skewer-html.el the CSS and HTML modes, skewer-bower.el the bower loader, cache-table.el a data structure, skewer.js the browser side script, skewer-everything.user.js the userscript, and example.html a sample document.

The Makefile is POSIX style and shows the dependencies concretely. It clones them into sibling directories, with a git clone of js2-mode into ../js2-mode and emacs-web-server into ../simple-httpd, and you can override the location with LDFLAGS. The compile target runs Emacs in batch mode with batch-byte-compile, and the run target launches Emacs with -Q, loads skewer-mode.elc and skewer-setup.elc, calls skewer-setup, sets initial-scratch-message to nil, loads js2-mode and calls run-skewer, which is the whole quick start in a Makefile target. The version in the Makefile is 1.9.0, and the package target builds a tarball from the Elisp files, the browser script, the example and the licence.

On maintenance, the repository is not archived and the last push was on 2026-09-04, and there are no GitHub releases at all, so the version you get is the version in your load-path, and the Makefile's 1.9.0 is the one number to look at. The MELPA package is the one to prefer, because the readme's own argument for MELPA is that it installs the dependencies, and the whole setup is two lines of Elisp and a browser.

## Conclusion

skewer-mode is worth adopting if you write front end code in Emacs and want the evaluate and reload loop of a browser console without leaving the editor, because the keybindings are the Lisp mode bindings you already know and the whole thing is one pure Elisp package with two dependencies. It is the wrong choice if you want a full development environment with a bundler, a source map and a dev server, because Skewer deliberately sits one level below that, and it is the wrong choice if you are on Emacs 24.2 or older, since the package requires 24.3 or later. Before you install it, read the CORS section if you intend to skewer pages on a server you do not control, and note that the last push was on 2026-09-04 with the package carrying no tagged releases.

## FAQ

### What does skewer-mode do?

It provides live interaction with JavaScript, CSS and HTML in a web browser, sending expressions on the fly from an editing buffer to be evaluated in the browser, the way Emacs uses an inferior Lisp process in Lisp modes. The browser acts as the remote interpreter and the result is echoed back into the minibuffer.

### How do I install skewer-mode?

Skewer is available from MELPA, which installs the dependencies for you, and the dependencies are simple-httpd and js2-mode, with the latter on ELPA. If you install from a checkout you put the repository directory in your load-path, load skewer-mode.el, and run M-x run-skewer. Skewer requires Emacs 24.3 or later.

### Which keybindings does skewer-mode use?

The Lisp mode bindings. C-x C-e evaluates the form before the point and shows the result in the minibuffer, or inserts it with a prefix argument, C-M-x evaluates the top-level form around the point, C-c C-k loads the current buffer, and C-c C-z selects the REPL buffer. CSS mode and HTML mode get similar bindings for the rules and tags on the page.

### Can skewer-mode work with a page on a server I do not control?

Yes, because Skewer supports CORS. You include the /skewer script in the document, and if you cannot modify the server at all you can use the provided Greasemonkey userscript, Skewer Everything, which assumes the server is at http://localhost:8080/ and injects Skewer once you switch the toggle in the top-right corner.

### What happens if the browser disconnects from Emacs while using skewer-mode?

Skewer's long polls from the browser can time out after a number of hours of inactivity, and the fix is to call skewer() in the browser console to reconnect. The documentation recommends this over a page reload, because a reload would lose any fragile browser state you might care about.

## Sources

- [Issues](https://github.com/skeeto/skewer-mode/issues)
- [License: Unlicense](https://github.com/skeeto/skewer-mode/blob/master/LICENSE)
- [README](https://github.com/skeeto/skewer-mode/blob/master/README.md)
- [skeeto/skewer-mode on GitHub](https://github.com/skeeto/skewer-mode)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/skeeto-skewer-mode
