I have had a few requests recently to dig into rendering images inside of Neovim and investigating whether there are any options that will work with tmux. Luckily I have found a solution!
Heads Up: I use Kitty as my terminal emulator and these examples were tested against ONLY that terminal
Background
There are many options when it comes to rendering images and getting a preview inside of your terminal AND inside of the greatest editor ever, Neovim.
Some attempts I made were using these different tools:
Kitty Graphics Protocol
This protocol was developed by the creator of the Kitty terminal emulator so that there could be a standard for rendering graphics in the terminal.
Terminal graphics protocol
The goal of this specification is to create a flexible and performant protocol that allows the program running in the…sw.kovidgoyal.net
a flexible and performant protocol that allows the program running in the terminal to render arbitrary pixel (raster) graphics to the screen of the terminal emulator.
Here are the goals :
Should not require terminal emulators to understand image formats.
Should allow specifying graphics to be drawn at individual pixel positions.
The graphics should integrate with the text, in particular it should be possible to draw graphics below as well as above the text, with alpha blending. The graphics should also scroll with the text, automatically.
Should use optimizations when the client is running on the same computer as the terminal emulator.
You can try out rendering an image by using the below command in your Kitty terminal
kitten icat /path/to/image.png
OR this one if it doesn’t work for you
kitty +kitten icat /path/to/image.png
This command works in tmux as well as non-tmux sessions.
Other terminals that have implemented the graphics protocol:
Image.nvim
In testing out MANY approaches, the only one that I could truly get working inside of tmux was image.nvim.
I’ll show you how I configured it and how you can get this up and running yourself.
Installation
Before we can install the plugin, we must first install some dependencies.
Install Imagemagick
brew install imagemagick
Install Lua 5.1
asdf plugin-add lua https://github.com/Stratus3D/asdf-lua.git
asdf install lua 5.1
echo "lua 5.1" >> ~/.tool-versions
When trying to install the plugin, it was locked to Lua 5.1. You might try not pinning to Lua 5.1 but this is what I needed to do for it to work.
Install magick via luarocks
We install this only for testing the minimal setup. We will use luarocks.nvim for setting it up in Neovim.
luarocks --local --lua-version 5.1 install magick
Allow tmux passthrough
set -gq allow-passthrough on
Before we install the plugin, let’s verify a minimal setup:
Navigate to this url on GitHub and copy the “Raw File” contents of the file into a file called “minimal-setup.lua” in your nvim directory.
image.nvim/minimal-setup.lua at master · 3rd/image.nvim
🖼️ Bringing images to Neovim. Contribute to 3rd/image.nvim development by creating an account on GitHub.github.com
Next, run this command to test the setup works:
nvim --clean -c ":luafile minimal-setup.lua"
You should see this for the minimal setup:
Note: If you see an error about magick not being loaded or “Failed to load ImageMagick (MagickWand)”, then install pkg-config (
brew install pkg-config
). Here’s a link to other errors.
Now that we know our minimal config works, let’s set it up in our own Neovim config.
I am using lazy.nvim so I added this to my plugins table:
{
"vhyrro/luarocks.nvim",
priority = 1001, -- this plugin needs to run before anything else
opts = {
rocks = { "magick" },
},
},
{
"3rd/image.nvim",
config = function()
require("image").setup({
backend = "kitty",
integrations = {
markdown = {
enabled = true,
clear_in_insert_mode = false,
download_remote_images = true,
only_render_image_at_cursor = false,
filetypes = { "markdown", "vimwiki" }, -- markdown extensions (ie. quarto) can go here
resolve_image_path = function (document_path, image_path, fallback)
return fallback(document_path, image_path)
end
},
html = {
enabled = false,
},
css = {
enabled = false,
},
},
max_width = nil,
max_height = nil,
max_width_window_percentage = nil,
max_height_window_percentage = 50,
window_overlap_clear_enabled = false, -- toggles images when windows are overlapped
window_overlap_clear_ft_ignore = { "cmp_menu", "cmp_docs", "" },
editor_only_render_when_focused = false, -- auto show/hide images when the editor gains/looses focus
tmux_show_only_in_active_window = false, -- auto show/hide images in the correct Tmux window (needs visual-activity off)
hijack_file_patterns = { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp", "*.avif" }, -- render image files as images when opened
})
end
}
Excellent! Now you should be able to view images from right within your Neovim instance after restarting Neovim.
Known Issues
I did discover an issue when the image was shown in Neovim and I swapped to another session:
Image is still shown when switched to another tmux window · Issue #106 · 3rd/image.nvim
❯ tmux -V tmux next-3.4 ❯ kitty --version kitty 0.31.0 created by Kovid Goyal ❯ cd ~/.local/share/nvim/lazy/image.nvim…github.com
I created the issue above and hope it gets resolved but in the meantime I updated the config to only show images that I have my cursor over instead of all of them:
...
markdown = {
...
only_render_image_at_cursor = true,
...
}
Getting it to work with Obsidian.nvim
For me, I was not able to get the images to render in my Obsidian vault in Neovim and needed to add the following hack to point to the right url:
This is in the markdown.lua file: ~/.local/share/nvim/lazy/image.nvim/lua/image/integrations/markdown.lua
elseif current_image and key == "url" then
current_image.url = "~/Documents/ObsidianVault/Attachments/" .. value
table.insert(images, current_image)
current_image = nil
end
Once there is a better solution or hook to override this then I will update this article.
Help with rendering images within ObsidianVault · Issue #190 · 3rd/image.nvim
Hi, thank you for all your work in getting images inside of Neovim. I am really excited to try and get this working. I…github.com
Other Approaches I tried
Oh man, I tried many different approaches in my search to get something working and almost always crashed on the solution not working in tmux.
Shout out to alpha2phi and his blog post that helped me with a few of these approaches
Neovim 101 — Image Viewer
The different ways to view images within Neovim.alpha2phi.medium.com
With most of these approaches, they have a “minimal-setup” file you can get started with to reduce the number of variables and make sure you have the dependencies correctly installed.
Most times this is leveraging the Kitty Graphics Protocol, uberzerg, imagemagick, chafa, viu, and other image libraries.
ranger (and ranger.nvim)
ranger is a separate file manager and is quite popular. Check it out if you haven’t tried it before.
Within ranger you can enable preview images and set the method to be kitty.
set preview_images true
set preview_images_method kitty
Once you do that, you should be able to open ranger (not in tmux), and see something like the below screenshot:
When I tried to bring in ranger into Neovim using ranger.nvim, I was not able to get the image previews to work :(
This was a no go from the beginning since it wouldn’t work in tmux but I figured I’d try to get as far as I could.
I don’t believe this is something that will be supported based on this Github issue:
Can preview image like ranger in Shell? · Issue #10 · kelly-lin/ranger.nvim
Hello, can I preview image byranger.nvim? Just like use ranger in Shell, for example this below, and what should I…github.com
Here’s my config for ranger.nvim in Neovim:
{
"kelly-lin/ranger.nvim",
config = function()
require("ranger-nvim").setup({ replace_netrw = true })
vim.api.nvim_set_keymap("n", "<leader>ef", "", {
noremap = true,
callback = function()
require("ranger-nvim").open(true)
end,
})
end,
}
hologram.nvim
A cross platform terminal image viewer for Neovim. Extensible and fast, written in Lua and C.
Works on macOS and Linux with current support for Kitty Graphics Protocol.
Highly experimental, expect breaking changes 🚧.
If you use kitty, you can also open up a terminal window and any image viewer that works in your regular terminal works there (sixel, uberzerg).
I was able to get this working outside of tmux but unfortunately not within tmux.
Here’s a link to the minimal setup:
GitHub - edluffy/hologram.nvim: 👻 A cross platform terminal image viewer for Neovim. Extensible…
👻 A cross platform terminal image viewer for Neovim. Extensible and fast, written in Lua and C. Works on macOS and…github.com
Here’s the config I got working:
{
'edluffy/hologram.nvim',
config = function()
require('hologram').setup {
auto_display = true -- WIP automatic markdown image display, may be prone to breaking
}
end
},
image_preview.nvim
I thought this one would be super easy to configure and get setup but it did not prove that way for me.
Here’s the config I tried:
{
'adelarsq/image_preview.nvim',
event = 'VeryLazy',
config = function()
require("image_preview").setup()
end
}
I was able to install chafa and viu using brew and got the fuzzy image preview to work but never the full resolution results.
This one DID work in tmux though so it might be worth revisiting.
I couldn’t get the image viewing to work in Obsidian though, so that would also need to be revisited.
Telescope Media Files Extension
I had really high hopes for this one. I would love to hook an image viewer into Telescope and thought this might be pretty easy.
Unfortunately, I couldn’t even get this one working outside of tmux, and likely wouldn’t work inside of tmux either.
Here’s the config I tried. Add this to lazy.nvim plugins:
{ 'nvim-telescope/telescope-media-files.nvim' },
Then add this to your telescope config:
telescope.setup({
extensions = {
media_files = {
-- defaults to {"png", "jpg", "mp4", "webm", "pdf"}
filetypes = { "png", "webp", "jpg", "jpeg" },
-- find command (defaults to `fd`)
find_cmd = "rg"
},
}
})
require("telescope").load_extension("media_files")
Make sure you are loading the extension and then you can invoke it with :Telescope media_files
.
It should then display a list of media files and show previews for each.
feh
This one just straight up didn’t work at all for me and I wasn’t interested in going much further with it.
Error I got stuck on:
feh ERROR: Can't open X display. It *is* running, yeah?
Conclusion
Even though the solution I have today is a bit hacked together, I was able to get a solution that worked in tmux for now and I can actually see images inside Neovim! That’s freakin’ sweet in my opinion. If you have other solutions or plugins that you use, definitely let me know so I can experiment with them as well.