Sync your zellij theme to whatever colorscheme is active in neovim.

It rewrites zellij's config file directly. zellij watches that file for changes and applies them to your already-running session automatically (zellij polls it roughly once a second), so this takes effect live — no restart needed.
Requires Neovim >= 0.10 (for the vim.health.* API used by :checkhealth).
See also :help zj-theme.
ColorScheme autocmd fires whenever you run :colorscheme ... (or a
plugin sets one on your behalf).vim.g.colors_name is looked up in a mapping table (built-in
defaults + anything you add) to find the corresponding zellij theme name.theme "..." line in zellij's config.kdl is rewritten in place with
that theme name.theme line to rewrite — it falls back to a
configurable default zellij theme instead of leaving things inconsistent.No external plugin dependency needed — zj-theme.nvim syncs every pane in
the session, including the one nvim itself runs in, on its own via the
zellij CLI. See Pane backgrounds for how that works.
-- For `plugins/zj-theme.lua` users.
return {
"jaimeibanezrivera/zj-theme",
lazy = false,
config = function()
require("zj-theme").setup({
-- see Configuration below
})
end,
}
-- For `plugins.lua` users.
{
"jaimeibanezrivera/zj-theme",
lazy = false,
config = function()
require("zj-theme").setup({
-- see Configuration below
})
end,
}
use({
"jaimeibanezrivera/zj-theme",
config = function()
require("zj-theme").setup({
-- see Configuration below
})
end,
})
Plug 'jaimeibanezrivera/zj-theme'
Then, elsewhere in your init.vim/init.lua (vim-plug has no per-plugin
config callback):
lua require("zj-theme").setup({
-- see Configuration below
})
local MiniDeps = require("mini.deps")
MiniDeps.add({ source = "jaimeibanezrivera/zj-theme" })
MiniDeps.now(function()
require("zj-theme").setup({
-- see Configuration below
})
end)
vim.pack.add({
"https://github.com/jaimeibanezrivera/zj-theme",
})
require("zj-theme").setup({
-- see Configuration below
})
{
dir = "~/path/to/zj-theme.nvim",
config = function()
require("zj-theme").setup({
-- see Configuration below
})
end,
}
require("zj-theme").setup({
-- Path to zellij's config.kdl. Defaults to ~/.config/zellij/config.kdl.
zellij_config_path = vim.fn.expand("~/.config/zellij/config.kdl"),
-- zellij themes to fall back to when the current colorscheme has no
-- mapping, or when writing zellij_config_path fails. Picked based on
-- vim.o.background so a dark colorscheme doesn't fall back to a light
-- zellij theme or vice versa.
default_dark_theme = "default",
default_light_theme = "pencil-light",
-- nvim colors_name -> zellij theme name. Merged over the built-in table
-- in lua/zj-theme/mappings.lua, so you only need overrides/additions.
-- A value can also be { dark = "...", light = "..." } for colorschemes
-- that keep vim.g.colors_name the same across light/dark (ayu.nvim,
-- gruvbox-material, everforest-nvim all do this, and are already handled
-- by the bundled defaults) — resolved via the current vim.o.background.
mappings = {
mycustomtheme = "my-zellij-theme-name",
myflexibletheme = { dark = "my-dark-zellij-theme", light = "my-light-zellij-theme" },
},
-- Whether to push the current colorscheme's bg/fg to every pane in the
-- zellij session, including the one nvim itself is running in. See
-- Pane backgrounds below. Set to false to leave every pane's background
-- alone.
sync_pane_backgrounds = true,
-- How often (ms) to poll for newly created zellij panes and color them
-- to match the current colorscheme — otherwise a pane opened after a
-- `:colorscheme` switch keeps zellij's default background until the next
-- one. Set to 0 to disable polling. Depends on the sync_pane_backgrounds
-- option. Not recommended to lower the number below the default value.
pane_poll_interval_ms = 1000,
-- Set to false to silence vim.notify warnings/errors.
notify = true,
})
Your config.kdl needs an actual (uncommented) theme "..." line already
present for this plugin to find and rewrite — it won't add one from
scratch.
Whatever "my-zellij-theme-name" you map a colorscheme to in mappings
has to actually exist for zellij, or nothing changes color once written.
Two ways to make that true:
themes { "my-zellij-theme-name" { ... } }
block in config.kdl (or a separate file in zellij's theme_dir) — see zellij's
theme documentation for the format.This plugin only rewrites the theme "..." line; it doesn't check whether
that name resolves to anything on zellij's side. Map to a theme that
doesn't exist (a typo, or one you meant to define but didn't) and zellij
will just silently fail to apply it — nothing will look wrong here, the
line will be written correctly, it just won't do anything.
:ZjThemeNow re-applies the mapping for whatever colorscheme is
currently active — useful for testing config changes without switching
colorschemes.
Rewriting zellij's theme only changes zellij's own colors — borders, tab
bar, status bar. It doesn't touch the background you actually see inside
each pane. zj-theme.nvim handles that itself, controlled by the
sync_pane_backgrounds option:
Every pane in the session, including the one nvim itself is running in,
gets recolored using a zellij command that can set a pane's background
directly (needs zellij 0.44 or newer). This is a property zellij holds for
that pane, not something tied to nvim's own process, which is why it
sticks around after nvim exits. This needs the zellij command to
actually be available on your system, not just a zellij session to be
running — :checkhealth zj-theme will tell you if it isn't.
Colors are never reset back to zellij's own defaults — once a pane is
colored, it stays that way, including after nvim exits, until the next
:colorscheme switch recolors it again. The goal is a session that keeps
looking like your last active colorscheme everywhere, not just while nvim
happens to be running in one of its panes.
Set sync_pane_backgrounds = false to turn this off.

Panes only get recolored when you change colorscheme or start nvim, so a
pane you open afterwards will briefly show zellij's default background
until it catches up. To smooth that over, a quiet background check runs
about once a second (pane_poll_interval_ms) looking for panes it hasn't
colored yet. Set pane_poll_interval_ms = 0 if you'd rather turn that off.
Getting rid of that brief delay entirely would take a small companion living inside zellij itself, reacting the moment a pane appears instead of checking every second or so — see Roadmap below.
My advice: make your terminal background as see-through as possible. That way the color swap won't feel so harsh. Your terminal's real background still shows through between the pane lines anyway, so it just looks better with lower opacity in general. Bonus: if you switch between light and dark mode with different wallpapers in your OS, this helps there too.
Best-effort mappings for these plugins are bundled by default — see
lua/zj-theme/mappings.lua for the exact
zellij theme each one resolves to, and override any of them via mappings
in setup().
| Plugin | Colorscheme names |
|---|---|
| catppuccin/nvim | catppuccin, catppuccin-mocha, catppuccin-macchiato, catppuccin-frappe, catppuccin-latte |
| ellisonleao/gruvbox.nvim | gruvbox |
| sainnhe/gruvbox-material | gruvbox-material |
| folke/tokyonight.nvim | tokyonight, tokyonight-night, tokyonight-storm, tokyonight-moon, tokyonight-day |
| EdenEast/nightfox.nvim | nightfox, dayfox, carbonfox |
| Shatur/neovim-ayu | ayu (ayu-dark/ayu-light/ayu-mirage are also mapped, for other ayu-family plugins that set those names directly) |
| maxmx03/solarized.nvim | solarized |
| lifepillar/vim-solarized8 | solarized8, solarized8_flat, solarized8_high, solarized8_low |
| navarasu/onedark.nvim | onedark |
| sonph/onehalf | onehalfdark (onehalflight has no zellij light-theme equivalent) |
| gbprod/nord.nvim | nord |
| Mofiqul/dracula.nvim | dracula |
| rebelot/kanagawa.nvim | kanagawa |
| neanias/everforest-nvim | everforest |
| NLKNguyen/papercolor-theme | PaperColor |
Using something else? Add it via mappings in setup() — see
Configuration.
default_dark_theme or default_light_theme (chosen by vim.o.background)
is used whenever:
$ZELLIJ unset) — no-op,
nothing is written at all.mappings.zellij_config_path doesn't exist/isn't readable, or has no theme "..."
line to rewrite.Run :checkhealth zj-theme to check whether setup() has been
called, whether you're currently inside a zellij session, whether
zellij_config_path exists and has a theme "..." line, whether the
active colorscheme is mapped, and — for
Pane backgrounds — whether sync_pane_backgrounds is
active, plus whether the zellij CLI is on PATH and its version is new
enough for set-pane-color.
zellij-tile WASM
plugin API) that reacts to a pane appearing instead of polling.
Nothing exists for this yet, but it's on my list to eventually build.Want to dig into the code or send a PR? See
ARCHITECTURE.md for a developer-facing walkthrough of
how the plugin is put together — the module breakdown, the event/data flow
through setup() and apply_all(), and diagrams for each of the two sync
mechanisms (zellij's own theme, every pane's background).
MIT, see LICENSE.