TL;DR. Download your artifact from Claude, rename the file to index.html, zip it, and deploy it at hatchable.com/deploy. You get a real public URL in about a minute. Then add a table and an API route (or ask your AI agent to) and the thing that used to forget everything on refresh starts remembering.
What an artifact is, and what it is missing
A Claude artifact is a self-contained page that runs inside a sandbox on claude.ai. It is genuinely useful: you describe a habit tracker or a pricing calculator or a client intake form, and a working interface appears next to the conversation.
Then you try to use it for real, and you hit the wall. As of July 2026:
- Nothing persists. Most artifacts hold their state in memory, so a refresh wipes the data. There is no database behind the page.
- It lives at a claude.ai address. You can share a link, but the artifact is a thing inside a chat product, not a site you own at your own domain.
- Everyone sees the same blank slate. Two people opening the same artifact are not looking at shared data, because there is no shared store to look at.
- No server side. No API routes, no scheduled jobs, no secrets you can safely hold, no way to email anyone.
None of that is a flaw in artifacts. A sandbox that renders a working UI in ten seconds is doing exactly its job. It is just that the job stops one step short of a product, and the missing step is hosting plus a database.
The two routes
There are two ways to close that gap, and they suit different moods.
| Route | What you do | Good for |
|---|---|---|
| Upload the file | Download the artifact, zip it, import it in the browser. No agent, no terminal, no code. | Getting the thing live in a minute, exactly as it looks now. |
| Hand it to an agent | Connect Claude Code (or Cursor, Codex, any MCP client) to Hatchable and say "put this on Hatchable and give it a database". | Everything past step one: storage, auth, iteration. |
Our advice is to do both, in that order. Upload first so you have a live URL to look at and share, then bring in an agent to add the database. The rest of this guide walks that path.
Part 1: get the artifact online
-
Get the file out of Claude
Open the artifact in claude.ai. Next to the Copy button there is a chevron with a download option, which saves the artifact as an
.htmlfile. (Anthropic moves this UI around, so if you do not see it, the reliable fallback is to ask in the chat: "Give me the complete, self-contained HTML for this artifact as a single file." Then paste the result into a text editor and save it.)What you want is one file that opens in a browser on its own. If Claude hands you a bare React component instead of a page, ask again for a single self-contained HTML file. That distinction matters later, and we come back to it in the gotchas.
-
Rename it to index.html
The importer looks for
index.htmlat the top level of the zip. Whatever the download was called (habit-tracker.html,claude-artifact-3.html), rename it. -
Zip it
On macOS, right click the file and choose Compress. On Windows, right click and choose Send to, then Compressed folder. From a terminal:
zip site.zip index.htmlIf your artifact came with extra assets (images, a CSS file, fonts), put them next to
index.htmland zip the whole folder. Zipping a folder is fine, the importer unwraps a single wrapper directory for you. -
Import it
Go to hatchable.com/deploy, sign in (free, no card), pick the "From a zip" tab and choose your file. Up to 50 MB per import.
Hatchable recognises a plain website, meaning an
index.htmlwith assets and no Hatchable manifest, and sets it up for you: your files move under the served directory, build junk likenode_modulesand lockfiles is dropped, the project name and description are lifted from your page's<title>and meta description, and ahatchable.tomlmanifest is written so the project exports cleanly later. -
Open the live URL
The first deploy is queued automatically. Thirty seconds or so later your artifact is a website at
https://your-project.hatchable.site, served from a real origin with real caching, not a preview pane.
Part 2: give it a database
This is the part that turns a demo into something people can use twice. The pattern is always the same three moves, whether you write them yourself or ask an agent to.
1. A table
Add a file under migrations/. They run in filename order on deploy, once each, forward only.
-- migrations/0001_create_habits.sql
CREATE TABLE habits (
id BIGSERIAL PRIMARY KEY,
user_id TEXT NOT NULL,
name TEXT NOT NULL,
color TEXT NOT NULL DEFAULT 'slate',
created_at TIMESTAMP NOT NULL DEFAULT now()
);
CREATE INDEX idx_habits_user_id ON habits(user_id);
2. An API route
Files under api/ become endpoints at the matching path. Each one declares who is allowed to call it, and that gate is enforced before your code runs.
// api/habits.js
import { db } from 'hatchable';
export const access = 'member'; // 'public' | 'member' | 'admin'
export const methods = ['GET', 'POST'];
export default async function (req, res) {
if (req.method === 'GET') {
const result = await db.query(
'SELECT id, name, color FROM habits WHERE user_id = $1 ORDER BY created_at',
[req.member.id]
);
return res.json({ habits: result.rows });
}
const name = (req.body.name || '').trim();
if (!name) return res.status(400).json({ error: 'Name is required.' });
const result = await db.query(
'INSERT INTO habits (user_id, name, color) VALUES ($1, $2, $3) RETURNING id, name, color',
[req.member.id, name, req.body.color || 'slate']
);
return res.status(201).json({ habit: result.rows[0] });
}
3. The swap in the page
Now find the place in the artifact where state was born and where it dies. Usually it is a useState([]) holding an array, plus a handler that pushes onto it. Load from the endpoint on mount, post to it on create, and the array stops being a lie:
// before: const [habits, setHabits] = useState([]);
// after:
const [habits, setHabits] = useState([]);
useEffect(() => {
fetch('/api/habits').then(r => r.json()).then(d => setHabits(d.habits));
}, []);
async function addHabit(name, color) {
const res = await fetch('/api/habits', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, color }),
});
const d = await res.json();
setHabits(h => [...h, d.habit]);
}
That is the whole conversion. Everything else in the artifact, the layout, the animations, the colour choices you argued Claude into, stays exactly as it was.
Who is "member", and do you need sign in?
The route above used access: 'member' and read req.member.id, which means every person gets their own habits and anonymous visitors never reach the handler. That is the right default for anything personal, like a tracker, a dashboard, a saved list.
If your artifact is meant to be open, a public leaderboard, a contact form, a shared guestbook, use access: 'public' and drop the user_id filter. Access is declared per file, so the common "anyone can read, only signed in people can write" split is just two files: a public GET route and a member POST route.
Sign in itself is a project setting, not something you build. Turn on auth and Hatchable handles the codes, the sessions, and the gate.
Gotchas specific to artifacts
The artifact was React, and the download is not a page
Some artifacts are a component rather than a document. If what you saved starts with import React from 'react' and exports a function, it is source code, not a website, and there is nothing for a browser to open. Ask Claude for a single self-contained HTML file, or hand the component to an agent and let it build the page around it. Note that Hatchable has no build step: JSX and TypeScript files are rejected at deploy, so React apps here use htm template literals instead of JSX. An agent connected to Hatchable knows this pattern and will do the translation for you.
The page loads React from a CDN
Artifacts commonly pull React, or anything else, from esm.sh or unpkg at runtime. It works on your machine and it is a real liability in production: any visitor whose network blocks that CDN, which corporate filters and DNS blockers do routinely, gets a blank page rather than a degraded one. We have watched it happen to a live dashboard. Fetch those libraries into your own project once, serve them from your own origin, and the failure mode disappears. Ask your agent to "vendor the CDN dependencies", it is a one time job.
The artifact called Claude directly
Artifacts can talk to Claude through helpers the claude.ai sandbox provides. Those helpers do not exist anywhere else, so any AI feature in your artifact goes quiet the moment it leaves the sandbox. On Hatchable you replace that call with a server side AI call and supply your own model key, which also means the key is never in the page where visitors can read it, and you can see exactly what you are spending.
localStorage was never really saving anything
If the artifact appeared to persist across a refresh, check what it was persisting into. Browser storage is per person and per device, so it is not a database: the data is invisible to you as the owner, invisible to every other visitor, and gone when the browser clears. Moving it into a table is the same swap shown above.
What you have when this is done
- A real public URL, with your own custom domain available on Builder.
- A Postgres database, with a browser in the console for reading and editing rows.
- Server side routes that can hold secrets, call other APIs, send email, and run on a schedule.
- Sign in, if you want it, without building auth.
- An export button. The project zips back out with its manifest and its data, because a thing you cannot leave with is not really yours.
And the loop you liked in the first place still works. Describe the change, let the agent make it, watch the live site update.
Turn your artifact into a real site.
Import a zip, get a live URL with a database, auth, and storage. Free to start, no card.
Get started free →Frequently asked questions
Do I need to know how to code to do this?
For part one, no. Downloading the artifact, renaming it to index.html, zipping it and uploading it is all browser work, and the result is a live public site. Part two, adding the database, involves a migration file and an API route, which you can either write yourself or ask an AI agent to write by connecting it to Hatchable over MCP. Most people do the second.
Will my artifact look exactly the same once it is live?
Yes, with one caveat worth checking. The HTML, CSS and layout carry over verbatim. If the artifact loaded libraries from a CDN at runtime, or called Claude through a helper that only exists inside claude.ai, those parts need rewiring. Both are covered in the gotchas above, and both are quick.
Can I import more than one file, like images and a stylesheet?
Yes. Put index.html at the top level with your assets alongside it and zip the folder. Everything is served from your project. Build artefacts like node_modules and lockfiles are dropped automatically, and anything skipped is listed for you after the import.
Is the site public as soon as I import it?
No. Imported projects are created private, so only you and people you invite can open them. Publishing is a deliberate click, "Publish to the web" on the project overview. Your first public app is free. Public sites show a small "Built on Hatchable" badge, which the Builder plan lets you switch off alongside custom domains.
Can I import a Figma or Claude Design export the same way?
Yes. Any plain website, meaning an index.html with assets and no build step, takes the same path: zip it, import it, and Hatchable sets up the project around it. The database steps in part two apply identically once it is in.
What happens to my data if I want to leave?
You export it. Every project downloads as a zip with its code and its hatchable.toml manifest, and its data downloads separately as SQL. Both re-import here, and the code is plain HTML, JavaScript and SQL that runs anywhere you can serve files and reach Postgres.