---
title: "WezTerm Hyperlinks (OSC-8)"
aliases: [wezterm-osc8, terminal-hyperlinks, wezterm-clickable-links]
tags: [wezterm, hyperlinks, osc8, lua, terminal, neovim, fish]
sources: [raw/Use hyperlinks directly in the terminal - Wez's Terminal Emulator.md]
created: 2026-04-17
updated: 2026-04-17
---

## WezTerm Hyperlinks (OSC-8)

WezTerm supports clickable hyperlinks in the terminal via **OSC-8 escape sequences**. With the right config, clicking a file link opens it in Neovim; clicking a directory `cd`s into it.

## How It Works

Terminal apps emit hyperlinks using the OSC-8 protocol (`\e]8;;URL\e\\text\e]8;;\e\\`). WezTerm intercepts clicks on these links via the `open-uri` event in Lua.

## Enabling Hyperlinks in CLI Tools

By default, tools like `ls` don't emit OSC-8. Add these aliases:

```sh
alias ls='ls --hyperlink --color=auto'
alias delta="delta --hyperlinks --hyperlinks-file-link-format='file://{path}#{line}'"
alias rg='rg --hyperlink-format=kitty'
```

> **macOS**: GNU `ls` (coreutils) required — install via Homebrew, or use [[wiki/dotfiles/linux-terminal-ricing|eza]] instead.

## open-uri Handler

The core Lua snippet hooks into `wezterm.on('open-uri', ...)` to route `file://` links:

| Condition | Action |
|-----------|--------|
| `file://` URI + pane is a shell + target is directory | `cd <dir>` + `ls` |
| `file://` URI + pane is a shell + target is text file | `nvim [+line] <file>` |
| `file://` URI + pane is NOT a shell (SSH, etc.) | Fallback shell one-liner |
| Any other URI | WezTerm default behavior |

### Shell Detection

```lua
local function is_shell(foreground_process_name)
  local shell_names = { 'bash', 'zsh', 'fish', 'sh', 'ksh', 'dash' }
  local process = string.match(foreground_process_name, '[^/\\]+$')
    or foreground_process_name
  for _, shell in ipairs(shell_names) do
    if process == shell then return true end
  end
  return false
end
```

Uses `pane:get_foreground_process_name()` — see [[wiki/dotfiles/wezterm-pane-to-lua|Pane to Lua]] for other pane data APIs.

### URI Format

`file://[HOSTNAME]/PATH[#linenr]` — the `#linenr` fragment is passed as `+N` to Neovim.

## Optional: Require CTRL to Click

Prevent accidental clicks by requiring a modifier:

```lua
config.mouse_bindings = {
  {
    event = { Up = { streak = 1, button = 'Left' } },
    mods = 'CTRL',
    action = act.OpenLinkAtMouseCursor,
  },
  -- Disable Down event to avoid weird behavior
  {
    event = { Down = { streak = 1, button = 'Left' } },
    mods = 'CTRL',
    action = act.Nop,
  },
}
```

See [[wiki/dotfiles/wezterm-mouse-bindings|Mouse Bindings]] for full mouse config reference.

## tmux Users

Enable the hyperlinks terminal feature in tmux config:

```sh
set -sa terminal-features ",*:hyperlinks"
```

May also need `Shift`+click depending on mouse reporting settings.

## Key Takeaways

- OSC-8 hyperlinks let CLI tools (`ls`, `rg`, `delta`) emit clickable file/dir links
- WezTerm's `open-uri` Lua event intercepts `file://` URIs for smart routing
- Click directory → auto `cd` + `ls`; click text file → open in Neovim at correct line
- SSH/non-shell panes fall back to a portable shell one-liner (slightly clutters prompt)
- CTRL+click modifier is optional but prevents accidental navigation
- tmux requires `terminal-features` opt-in for hyperlinks to pass through

## Drawbacks

- Only works when the active pane is at an **empty shell prompt** — won't work inside vim, less, etc.
- Existing text in the prompt will be corrupted if a hyperlink is clicked mid-edit
- The non-shell fallback emits a long command visible in the terminal history

## Sources

- `raw/Use hyperlinks directly in the terminal - Wez's Terminal Emulator.md`
- Source URL: https://wezterm.org/recipes/hyperlinks.html
