Save Substack posts for offline reading with assets preserved, from a popup-first Chrome extension.
- Popup-first UI (no "Open Full App in Tab" action in normal user flow).
- Batch save by number of posts or date range.
- Save the currently open post from the active tab.
- Closable toast notifications plus structured console logs.
- Offline-friendly snapshot rewriting for images, embeds, and media fallbacks.
- Click the extension icon to open the popup.
Publication URLauto-populates when the active tab can be resolved to a publication.Save Postsuses a one-click folder flow: choose a folder once per batch, then posts are written as.htmlfiles into that folder.Save Current Postsaves the active post route directly.- Saved post titles are clickable to open.
- Delete controls include a trash icon per post and
Delete Allper publication.
Save Current Post and publication detection handle:
- Canonical post URLs:
https://<publication>/p/<slug> - Substack root post routes:
https://substack.com/home/post/p-<id> - Substack root post routes:
https://substack.com/inbox/post/<id> - Profile routes for publication resolution:
https://substack.com/@<handle> - Substack/custom domains where post preview metadata resolves the canonical post.
Default commands from manifest.json:
Alt+Shift+Eopens the extension popup (_execute_action).Alt+Shift+Ssaves the currently open Substack post (save-active-post).
You can change shortcuts at chrome://extensions/shortcuts.
- Node.js
>= 24.11 < 25(Volta pinned to24.13.0). - npm.
- Chrome or Chromium browser with Manifest V3 support.
- Logged-in Substack session in the same browser profile.
npm installnpm run buildBuild output:
build/extension-package
- Open
chrome://extensions. - Enable Developer mode.
- Click Load unpacked.
- Select
build/extension-package.
- Settings and UI metadata are stored in
chrome.storage.local. - Batch folder-save posts are stored in your chosen folder via File System Access API.
- Active post saves and persisted snapshots are written through Chrome Downloads under
Downloads/Substack Offline Saver/<publication>/....
- UI messages appear as short-lived toasts with a
Closeaction. - The same events are logged to console with
[substack-offline/ui]and metadata. - Logged metadata includes fields such as
event,level,message, publication URL, post ID/title, save counts, and job ID when available.
npm run lint- ESLint.npm run test- unit tests (Vitest).npm run test:e2e- live e2e tests.npm run build- TypeScript build + UI build + extension packaging.npm run build:ui- UI bundle only.npm run build:watch- TypeScript watch mode.npm run start- print packaged extension path.npm run start:cli- run legacy CLI output.
Formatting enforcement in validation and regression loops:
pretestrunsnpx prettier --write ..prebuildrunsnpx prettier --write .and lint.
- Make changes.
- Run
npm run test. - Run
npm run build. - Reload the extension in
chrome://extensions.
- Ensure you are on a real post route (
/inbox/post/<id>or/home/post/p-<id>). - Rebuild and reload the extension to pick up latest inbox-resolution logic.
- Log in at
https://substack.comin the same browser profile.
- Confirm
downloadsanddownloads.openpermissions are present in the loaded build. - Reload extension after upgrading.
- Confirm you selected
build/extension-package.
- Do not share browser profile data.
- Do not share exported extension storage blindly.
- The extension uses your own authenticated session to fetch content you can already access.