electron-libghostty
October 30, 2025 · View on GitHub

Native libghostty terminal renderer for Electron
Embed high-performance Ghostty terminal surfaces directly in Electron windows
Installation • Quick Start • API • Examples • Contributing
⚠️ Preview Release: This is a preview release. We're planning to migrate to Shared Texture for improved performance in future versions.
✨ Features
- 🖥️ Native Terminal Rendering – Powered by libghostty for native-quality terminal emulation
- ⚡ High Performance – Direct rendering to native macOS views
- 🎯 Precise Positioning – Overlay terminal surfaces anywhere in your Electron UI
- 📦 Modern Package – Dual ESM/CommonJS support with TypeScript declarations
🚀 Installation
pnpm add electron-libghostty
Requirements
- macOS 13 Ventura or later
- Electron 30+
- Node.js 22+
- Zig 0.14.0+ (only required when building libghostty locally)
Note: This package only works on macOS.
🎯 Quick Start
Main Process (Electron)
import { app, BrowserWindow, ipcMain } from 'electron'
import ghosttyHost from 'electron-libghostty'
let mainWindow: BrowserWindow
let surfaceId: number | null = null
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
})
mainWindow.loadFile('index.html')
// Listen for events from the terminal
ghosttyHost.onEvent((event) => {
console.log('[ghostty] event', event)
})
// Handle focus/blur
mainWindow.on('focus', () => {
if (surfaceId !== null) {
ghosttyHost.setFocus(surfaceId, true)
}
})
mainWindow.on('blur', () => {
if (surfaceId !== null) {
ghosttyHost.setFocus(surfaceId, false)
}
})
})
// Create terminal surface at specified bounds
ipcMain.on('native-overlay:update', (_event, rect) => {
if (surfaceId === null) {
surfaceId = ghosttyHost.create(
mainWindow.getNativeWindowHandle(),
rect
)
} else {
ghosttyHost.resize(surfaceId, rect)
}
})
// Clean up terminal surface
ipcMain.on('native-overlay:hide', () => {
if (surfaceId !== null) {
ghosttyHost.destroy(surfaceId)
surfaceId = null
}
})
Renderer Process
const { ipcRenderer } = require('electron')
// Calculate terminal bounds from a DOM element
function getTargetRect() {
const target = document.querySelector('#terminal-container')
const rect = target.getBoundingClientRect()
const scale = window.devicePixelRatio || 1
return {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
scale,
}
}
// Update terminal position
function updateTerminal() {
const rect = getTargetRect()
ipcRenderer.send('native-overlay:update', rect)
}
// Track element position changes
window.addEventListener('DOMContentLoaded', () => {
const target = document.querySelector('#terminal-container')
// Update on resize
new ResizeObserver(() => updateTerminal()).observe(target)
// Update on scroll
window.addEventListener('scroll', updateTerminal, { passive: true })
// Initial update
updateTerminal()
})
📚 API Reference
ghosttyHost.create(handle, rect)
Creates a new terminal surface overlay.
Parameters:
handle: Buffer- The native window handle fromBrowserWindow.getNativeWindowHandle()rect: { x: number, y: number, width: number, height: number, scale?: number }- Position and size
Returns: number - Surface ID for future operations
ghosttyHost.resize(surfaceId, rect)
Updates the position and size of an existing terminal surface.
Parameters:
surfaceId: number- The surface ID returned fromcreate()rect: { x: number, y: number, width: number, height: number, scale?: number }- New bounds
ghosttyHost.destroy(surfaceId)
Destroys a terminal surface.
Parameters:
surfaceId: number- The surface ID to destroy
ghosttyHost.setFocus(surfaceId, focused)
Sets the focus state of a terminal surface.
Parameters:
surfaceId: number- The surface IDfocused: boolean- Whether the surface should be focused
ghosttyHost.setOccluded(surfaceId, occluded)
Sets the occlusion state (visibility) of a terminal surface.
Parameters:
surfaceId: number- The surface IDoccluded: boolean- Whether the surface is occluded (hidden)
ghosttyHost.onEvent(callback)
Registers a callback for terminal events.
Parameters:
callback: (event: any) => void- Event handler function
🏗️ How It Works
electron-libghostty embeds native Ghostty terminal surfaces as overlays within Electron windows:
- Native Terminal: Uses libghostty's native macOS rendering for high-performance terminal emulation
- Overlay Positioning: Terminal surfaces are positioned over Electron web content at specified coordinates
- Event Handling: Keyboard and mouse events are properly routed to the terminal surface
- Lifecycle Management: Surfaces can be created, resized, focused, and destroyed dynamically
See the examples/ directory for a complete working implementation.
🔧 Development
Building from Source
git clone https://github.com/meridius-labs/electron-libghostty.git
cd electron-libghostty
pnpm install
# Build native module
pnpm run build:native
# Build TypeScript library
pnpm run build
# Build everything
pnpm run build:all
Project Structure
electron-libghostty/
├── src/ # Native Objective-C++ source
│ ├── overlay_view.mm # Terminal surface overlay
│ └── liquidglass.cc # Node.js addon bindings
├── js/ # TypeScript source
│ └── index.ts # Main library code
├── third_party/libghostty/ # Ghostty terminal library
├── examples/ # Example application
└── dist/ # Built library (generated)
🤝 Contributing
Contributions are welcome! Please open an issue or PR on GitHub.
🙏 Acknowledgments
- Ghostty - The excellent terminal emulator that powers this library
- The Electron team for native integration capabilities
📄 License
MIT © Meridius Labs 2025
Made with ❤️ for the Electron community