Skip to content

Repository files navigation

Zen Sync — export your tabs, spaces and folders, then import them anywhere, exactly as you left them

Platforms Python 3.9+ Tested on Zen 1.21 MIT licence Sponsor

Quick start

Nothing to download, nothing to install.

macOS / Linux — paste into Terminal:

curl -fsSL https://raw.githubusercontent.com/nrb-dev/zen-sync/main/bootstrap.sh | bash

Windows — paste into PowerShell:

irm https://raw.githubusercontent.com/nrb-dev/zen-sync/main/bootstrap.ps1 | iex

Either one drops you into a small menu:

    1  Back up my sidebar now
    2  Restore my sidebar from a backup
    3  Show what is in my sidebar
    q  Quit

Backups land in Documents/Zen Backups/<date>/. Keep the folder somewhere safe — iCloud, OneDrive, a USB stick. To restore after reinstalling Zen: quit Zen completely, run the same one-liner, choose 2. It finds your newest backup on its own.

The bootstrapper downloads zen_pins.py into a temp folder, runs it, and deletes it. It installs nothing. If you'd rather read the code first, that's the whole point of the URL — open it in a browser before you run it.

Skip the menu and run a step directly
curl -fsSL https://raw.githubusercontent.com/nrb-dev/zen-sync/main/bootstrap.sh | bash -s -- backup
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/nrb-dev/zen-sync/main/bootstrap.ps1))) backup
Or clone the repo and double-click (macOS)
  1. 1 · Back up Zen sidebar.command — saves everything into ~/Documents/Zen Backups/<date>/ and opens that folder.
  2. 2 · Restore Zen sidebar.command — quit Zen first (Cmd-Q), then double-click.

The first time, macOS may say the file is from an unidentified developer: right-click it → OpenOpen. That's only needed once per file. If double-clicking opens the file in a text editor instead of running it, right-click → Open WithTerminal.

What it saves

Saved Not saved
Essentials, grouped per container Open, unpinned tabs and their history
Pinned tabs, in sidebar order Cookies and logins
Pinned folders, including collapsed state Extensions
Spaces — names, icons, gradient themes Bookmarks
Custom pin labels and favicons userChrome.css

Everything not saved lives elsewhere in the profile — for a full move, copy the whole profile directory instead.

The visual index

Every backup includes My Zen sidebar.html: a self-contained page laid out like Zen itself, with an Essentials rail on the left and your Spaces on the right. Favicons are embedded, it's light/dark aware, it makes no network requests, and it still opens years from now with no Zen involved.

The rail rides beside whichever Space is active — resting level with that Space's card, springing across when the active Space changes, and pinning to the top of the viewport once the card scrolls past so it never leaves the screen. The set it shows swaps with the Space's container, the same swap Zen does when you change Space, and it picks up that Space's gradient. Below 900px, and when printing, it collapses to one column listing every container's set in full. The spring is skipped entirely under prefers-reduced-motion.

Why a script and not an extension

WebExtensions can't see any of this. browser.tabs reports pinned tabs of a running window only, and Essentials, pinned folders and Spaces are Zen-specific chrome state with no extension API at all. The data lives in one file in the profile, so a script that reads that file is the only thing that can capture it completely.

Where the data lives
<profile>/zen-sessions.jsonlz4

A mozlz4 file (LZ4 block + an 8-byte mozLz40\0 header) containing:

key what it holds
spaces Workspaces: uuid, name, icon, gradient theme, container
folders Pinned folders: name, parent, sibling order, collapsed, workspace
groups Tab-group records the folders are backed by
tabs Every tab; pins carry pinned, zenEssential, zenWorkspace, groupId
splitViewData Split-view layouts

A pinned tab's "home" URL — what reset pin goes back to — is _zenPinnedInitialState, which is what this tool exports rather than whatever the tab happens to be showing.

Essentials belong to containers, not Spaces

Every Space using the same container shows the same Essentials. A tab's zenWorkspace field only records where it was created, so grouping Essentials by Space would list the same tabs several times and imply an isolation that isn't there. The exported page and the list output group them by container instead, naming the Spaces that share each set.

Two Spaces on different containers can hold what look like duplicates — the same site pinned twice, once per container, with separate logins. Those are genuinely two pins and both are kept. Container names come from the profile's containers.json.

Profile locations

OS Path
macOS ~/Library/Application Support/zen/Profiles/
Windows %APPDATA%\zen\Profiles\
Linux ~/.zen/ (or the Flatpak path under ~/.var/app/)

Backups go to your Documents folder, following the OneDrive redirect on Windows and iCloud's Desktop & Documents sync on macOS.

Command reference

The one-liners and the .command files are thin wrappers around this. Run it with no arguments for the same menu, or drive it directly:

python3 zen_pins.py backup            # save to Documents/Zen Backups/<date>/
python3 zen_pins.py restore           # put the newest backup back (asks first)
python3 zen_pins.py restore path/to/zen-pins.json

backup and restore are the friendly wrappers; export and import are the same thing with every knob exposed:

python3 zen_pins.py profiles          # list detected Zen profiles
python3 zen_pins.py list              # preview what would be exported
python3 zen_pins.py export -o backup.json --html sidebar.html
python3 zen_pins.py import backup.json

Restoring needs Zen fully quit — it refuses to run otherwise, because Zen overwrites the file with its in-memory state on exit. Preview without writing anything with --dry-run.

Options

  • --profile NAME|PATH — pick a profile explicitly; otherwise the one with the most recently written sidebar data is used.
  • --html FILE — also write the visual index described above.
  • --bookmarks FILE — also write a plain Netscape bookmark file. Insurance: if Zen ever changes its format, you can still import your links into any browser.
  • --raw — copy zen-sessions.jsonlz4 verbatim instead of building a bundle. Perfect fidelity including full tab history, but only restorable into the same Zen version.
  • --fresh (import) — also drop unpinned tabs already in the target profile. By default they're kept and re-homed into your first Space.
  • --force (import) — proceed when the system can't report whether Zen is running. The check uses ps on macOS/Linux and tasklist on Windows; if neither answers, the import stops rather than risk running underneath a live Zen.
What a restore actually does
  1. Refuses to run while Zen is running.
  2. Backs up zen-sessions.jsonlz4 and prefs.js with timestamped .bak-* names.
  3. Writes spaces, folders, groups and pins from the bundle; rebuilds each pin as a one-entry session tab (sync id, favicon, custom label, container and folder preserved).
  4. Moves zen-sessions-backup/ aside, so Zen can't restore the profile's old state over the import.
  5. Points zen.workspaces.active at a Space that exists in the imported set.

Every step is reversible: delete what was written and rename the .bak-* files back.

Verified end to end

Tested by exporting a live profile (4 Spaces, 4 pinned folders, 48 pins, 13 Essentials), importing into an empty profile, running Zen against it and reading back what Zen itself saved on quit. All 48 pins, both Docs folders, every Space name/icon/gradient, all 27 custom pin labels and all folder assignments survived. Two things to expect:

  • Zen's first run on a brand-new profile adds its own "zen basics" folder and welcome pin alongside your imported ones. Delete it in the sidebar.
  • Zen re-orders the tab array internally when it saves, but sidebar order within each Space and folder is preserved exactly. Some favicons are re-fetched on first load.
Format notes

The bundle is plain JSON with a version field, so it stays readable even if Zen's internal format shifts; source.zenVersion records the Zen build it came from. Favicons are embedded as data URIs, which is most of the file size.

Zen stores some pin icons as an empty moz-remote-image:// placeholder rather than the image itself. For those, export falls back to the profile's own icon cache (favicons.sqlite, read from a copy so a running Zen isn't disturbed) and embeds the real icon — which is why the HTML page has a favicon for every pin rather than for two thirds of them.

Support

Zen Sync is free and always will be. If it saved you an evening of rebuilding your sidebar by hand, you can sponsor the work on GitHub — one-off or monthly, whatever suits.

Starring the repo and telling other Zen users about it helps just as much.

License

MIT — use it, fork it, ship it, just keep the copyright notice.

The Zen name and logo belong to the Zen Browser project and are not covered by this licence.

About

Allow Zen browser spaces/essentials/pinned tabs/folders import/export.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages