# Understanding the Lazygit Keybinding System: How to Create Custom Keybindings

> Master the lazygit keybinding system to create custom shortcuts. Learn how to define custom commands effortlessly in config.yml or programmatically for a streamlined workflow.

- Repository: [Jesse Duffield/lazygit](https://github.com/jesseduffield/lazygit)
- Tags: deep-dive
- Published: 2026-03-02

---

**The lazygit keybinding system translates human-readable labels like `<c-p>` into low-level key codes using the `GetKey` function, enabling users to define custom shortcuts by adding `customCommands` entries to [`config.yml`](https://github.com/jesseduffield/lazygit/blob/main/config.yml) or creating `Binding` structs programmatically.**

The lazygit keybinding system provides a flexible, type-safe architecture built on the gocui UI library that separates declarative configuration from runtime execution. By mapping human-readable labels to internal key representations, the system allows you to bind any command—built-in or user-defined—to specific keys within particular views or globally across the application.

## How the Lazygit Keybinding System Works

At its core, the lazygit keybinding system bridges human-readable configuration and the low-level key codes used by the underlying gocui UI library. The architecture centers on the `Binding` struct, which encapsulates everything needed to associate a key press with an action.

### Core Types and Structures

The foundation of the system resides in [`pkg/gui/types/keybindings.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/types/keybindings.go), which defines the `Binding` struct that represents a single keybinding:

```go
type Binding struct {
    ViewName          string                // view that must have focus; empty = global
    Handler           func() error          // command executed on key press
    Key               Key                   // rune or gocui.Key
    Modifier          gocui.Modifier        // e.g. ModAlt, ModCtrl (rarely used)
    Description       string                // shown in the cheatsheet
    // … additional UI fields (short description, tags, display style, tooltip, etc.)
}

```

The `Key` type is defined as `type Key any` and can hold either a `rune` for character keys or a `gocui.Key` for special keys like function keys or arrow keys. When you define a custom keybinding in your configuration, the `KeybindingCreator` in [`pkg/gui/services/custom_commands/keybinding_creator.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/services/custom_commands/keybinding_creator.go) constructs one of these `Binding` structs by resolving your string label into the appropriate `Key` value.

### Label-to-Key Translation

The translation between configuration labels and runtime keys happens in [`pkg/gui/keybindings/keybindings.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/keybindings/keybindings.go). The `GetKey` function converts string labels like `<c-p>` or `<f5>` into the internal `Key` type used by gocui:

- If the label is longer than one rune (e.g., `<c-p>`), the function looks it up in `config.KeyByLabel` from [`pkg/config/keynames.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/keynames.go).
- If it is a single rune, it returns the rune directly.
- The special label `<disabled>` yields `nil`, effectively disabling the binding.

For reverse lookup (used in UI rendering such as the cheatsheet), the `LabelFromKey` function consults `config.LabelByKey` to convert internal key representations back to human-readable strings.

## Creating Custom Keybindings in Lazygit

You can create custom keybindings either through declarative configuration in [`config.yml`](https://github.com/jesseduffield/lazygit/blob/main/config.yml) or programmatically if you are extending lazygit's source code.

### Configuring Custom Commands via config.yml

The simplest way to add custom keybindings is to edit your `~/.config/lazygit/config.yml` file. The `customCommands` array allows you to bind shell commands to specific keys within defined contexts:

```yaml
customCommands:
  # Example: Pull the current branch with Ctrl-P in the Files view

  - key: "<c-p>"
    context: "files"
    command: "git pull"
    description: "Pull latest changes"

```

When lazygit loads, it parses this configuration into a `config.CustomCommand` struct. The `KeybindingCreator` then validates the context, calls `keybindings.GetKey(customCommand.Key)` to resolve `<c-p>` into an actual `Key` value, and produces a `*types.Binding` object linked to the command's handler. Restart lazygit or press `Ctrl+R` to reload the configuration and activate the new binding.

### Disabling Existing Bindings

To disable a keybinding without removing the configuration entry, set the key to `<disabled>`:

```yaml
- key: "<disabled>"
  context: "files"
  command: "git pull"
  description: "Pull latest changes (disabled)"

```

The `GetKey` function returns `nil` for this label, causing the binding to be ignored while preserving the entry for future re-enablement.

### Programmatic Keybindings for Developers

If you are contributing to lazygit or building a fork, you can create bindings directly in Go. Import the necessary packages and construct a `Binding` struct:

```go
import (
    "github.com/jesseduffield/gocui"
    "github.com/jesseduffield/lazygit/pkg/gui/keybindings"
    "github.com/jesseduffield/lazygit/pkg/gui/types"
)

func newMyBinding() *types.Binding {
    return &types.Binding{
        ViewName:    "status",               // empty string = global
        Key:         keybindings.GetKey("<c-m>"), // Resolve label → Key
        Modifier:    gocui.ModNone,
        Handler: func() error {
            // Your command logic here
            return nil
        },
        Description: "My custom action",
    }
}

```

Register this binding with the UI's `Gui` object by appending it to the keybindings collection, as implemented in the registration loop within [`pkg/gui/keybindings/keybindings.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/keybindings/keybindings.go).

## Practical Examples for Common Use Cases

### Global Shortcut to Open Help

To create a binding that works across all views, set the context to `global` (which maps to an empty `ViewName` internally):

```yaml
customCommands:
  - key: "<f1>"
    context: "global"
    command: "help"
    description: "Open the Help panel"

```

The `<f1>` label resolves to `gocui.KeyF1` via the `LabelByKey` map in [`pkg/config/keynames.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/keynames.go).

### Context-Specific Script Execution

Bind a custom script to run only when the Commits view has focus:

```yaml
customCommands:
  - key: "<c-e>"
    context: "commits"
    command: "./scripts/checkout-branch.sh"
    description: "Checkout selected commit's branch"

```

The `KeybindingCreator` maps the `commits` context string to the appropriate view name, ensuring the handler only executes when that panel is active.

### Programmatic Toggle Binding

For developers extending the application directly:

```go
func (gui *Gui) addToggleStashBinding() {
    binding := &types.Binding{
        ViewName:    "status",
        Key:         keybindings.GetKey("<c-b>"),
        Modifier:    gocui.ModNone,
        Handler:     gui.toggleStash,
        Description: "Toggle stash view",
    }
    gui.State.Keybindings = append(gui.State.Keybindings, binding)
}

```

## Summary

- The **lazygit keybinding system** uses the `Binding` struct in [`pkg/gui/types/keybindings.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/types/keybindings.go) to associate keys with handlers and view contexts.
- **Label translation** occurs through `GetKey` and `LabelFromKey` in [`pkg/gui/keybindings/keybindings.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/keybindings/keybindings.go), using lookup tables defined in [`pkg/config/keynames.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/keynames.go).
- **Custom commands** are created by adding entries to `customCommands` in [`config.yml`](https://github.com/jesseduffield/lazygit/blob/main/config.yml), which the `KeybindingCreator` processes into runtime bindings.
- **Global bindings** use an empty `ViewName` or the `global` context, while specific bindings target individual views like `files` or `commits`.
- **Disabling bindings** requires setting the key to `<disabled>`, which causes `GetKey` to return `nil`.

## Frequently Asked Questions

### How do I find the correct context name for a specific view?

The context names used in [`config.yml`](https://github.com/jesseduffield/lazygit/blob/main/config.yml) correspond to the view names defined in lazygit's source code. Common values include `files`, `commits`, `branches`, `status`, and `global`. Refer to the `KeybindingCreator` implementation in [`pkg/gui/services/custom_commands/keybinding_creator.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/services/custom_commands/keybinding_creator.go) or the documentation in [`docs/keybindings/Custom_Keybindings.md`](https://github.com/jesseduffield/lazygit/blob/main/docs/keybindings/Custom_Keybindings.md) for the complete list of valid context strings.

### What is the difference between global and view-specific keybindings?

Global keybindings have an empty `ViewName` field (specified as `context: global` in config) and are active regardless of which panel has focus. View-specific bindings only trigger when their designated view is active, allowing you to reuse the same key for different actions in different contexts. The `Binding` struct's `ViewName` field controls this behavior.

### How do I disable a default lazygit keybinding without modifying the source code?

While you cannot directly override built-in bindings through `customCommands`, you can disable custom command bindings by setting `key: "<disabled>"` in your configuration. This causes the `GetKey` function to return `nil`, preventing the binding from registering. For built-in bindings, you would need to fork the repository and modify the registration logic in [`pkg/gui/keybindings/keybindings.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/keybindings/keybindings.go).

### Can I bind multiple keys to the same custom command?

Yes, you can define multiple entries in the `customCommands` array that reference the same command but specify different keys and potentially different contexts. Each entry creates a separate `Binding` struct through the `KeybindingCreator`, allowing you to trigger the same shell command or script from different views using different key combinations.