The Problem: Claude Artifacts are Stuck in the Chat
We've all been there. You prompt Claude to build a neat little todo list, a habit tracker, or a dashboard. It spits out a beautiful React/HTML artifact. You play with it, think "This is actually useful", and then... you close the tab.
It's gone.
If you do save the HTML, turning it into a proper installable mobile app is a nightmare:
- Wrestle with Webpack/Vite configs just to get a
manifest.json. - Write a Service Worker from scratch.
- Pray your
localStoragedoesn't get randomly wiped by the OS when the PWA updates.
I built @baaqar/artifact-to-pwa to fix all of that in one command.
The Solution: npx artifact-to-pwa
No build step. No Android Studio. No Xcode. Just point it at your file or URL.
# From a local React/HTML file
npx artifact-to-pwa ./my-app.jsx
# From a published Claude artifact URL
npx artifact-to-pwa https://claude.site/artifacts/abc123
# Customize it
npx artifact-to-pwa ./app.html --name "My Tool" --color "#ff6b6b" --out ./dist
That's it. It generates a fully production-ready PWA folder:
my-app-pwa/
├── index.html ← Your app, PWA-ready
├── manifest.json ← Icons, colors, display mode
├── sw.js ← Service worker (offline support)
├── icon.svg ← Auto-generated icon
└── README.md ← One-click deploy instructions
The Killer Feature: Persistent Storage (localStorage → IndexedDB)
Here is where this package actually shines and differs from every other "PWA wrapper" out there.
If your artifact uses localStorage (think: todo lists, streak trackers, user settings, heatmaps), the browser will eventually delete that data. Worse, when a user updates the PWA, the cache clears, and poof—their entire history is gone.
artifact-to-pwa automatically detects localStorage usage and injects a transparent IndexedDB shim behind the scenes.
- Your artifact keeps using the exact same
localStorageAPI (no code changes needed). - All data is actually stored in IndexedDB, which survives PWA updates and OS cleanups.
- It pre-populates an in-memory cache on load so reads stay lightning-fast.
- It briefly hides the page on startup until saved data is ready—preventing that ugly "empty state" flash.
You'll see this magic happen in your terminal:
⚡ localStorage detected → auto-migrating to IndexedDB
How to Install It on Your Phone
Once you run the command, deploying it is drag-and-drop simple:
| Host | Command / Action |
|---|---|
| Netlify | Drag the folder to netlify.com/drop |
| Vercel | npx vercel my-app-pwa |
| GitHub Pages | Push folder contents, enable Pages in settings |
Desktop / Android: npx serve my-app-pwa → Open URL → Click the Install icon in the address bar.
iPhone / iPad: Deploy anywhere → Open in Safari → Share → Add to Home Screen.
Why Bother with a PWA instead of an APK?
- Cross-platform: Installs on Android, iOS, Windows, macOS, and Linux simultaneously.
- Always up-to-date: Users get the latest version automatically—no app store review delays.
- Offline-first: The built-in Service Worker caches all assets.
Supported Formats
This tool is smart enough to detect exactly what you throw at it:
-
Full HTML documents (
<!DOCTYPE html>) - HTML fragments (partial markup)
- React / JSX (uses Babel standalone + CDN React—no bundler needed!)
- Public URLs (embeds them as a full-screen iframe)
Give it a Spin
If you build with Claude, or if you just hate configuring Webpack manifests, give this a try:
👉 Check it out on GitHub
👉 Install from NPM
Drop a star ⭐ if it saves you time, and let me know what you build with it!
Made with ❤️ to keep Claude artifacts alive outside the chat.
Top comments (7)
The localStorage to IndexedDB shim is the clever part, and keeping a synchronous API on top of an async store is a real trick. Preloading a cache on startup makes reads work, that part's clear. What I'm genuinely unsure about is writes. Since
setItemhas to return right away but IndexedDB persists asynchronously, isn't there a small window where a crash right after a write loses that last value? For a streak tracker that's fine, but for data you'd hate to lose it might be worth a line in the README.You're right. setItem updates the mirror sync, then queues an IndexedDB put(). The transaction commits after the current task finishes, so writes aren't disk-durable for a few milliseconds.
Practically: tab close/navigation is safe (browser waits), but an OS/browser crash in that gap loses the last write. For streak counters/themes, it's negligible. For form drafts or finance, it matters.
I'll add a README caveat and explore transaction.oncomplete for a "synced" indicator in write-heavy apps. Appreciate the precise flag!
This solves a real gap in the artifact workflow. The moment an artifact becomes useful, the next question is distribution: installability, persistence, offline behavior, and updates. Zero-config PWA export is a nice bridge between demo and actual utility.
Thanks. I'm working with multiple artifacts and was wondering how to use them without publishing, thus this came into being.
That makes sense. Artifacts are useful while you are exploring, but the moment you want to reuse them across devices or share them without publishing friction, they need a deployable wrapper. The zero-config part is what makes that workflow feel less like a separate product step.
This is fascinating! I'm curious how
Thanks.
The details are mentioned in the readme file. Feel free to check it out.