> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inklyai.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish and self-host

> Get a demo online with one command, or build a static folder and host it yourself.

A demo page needs a URL before you can send it or embed it. There are two ways to get one. The [embed snippets](/open-source/embedding) are the same either way. Only the origin differs.

| | Publish | Host it yourself |
| - | - | - |
| Command | `interactive-demo publish` | `interactive-demo build` |
| Where it lives | `interactive-demo.inklyai.dev` | Any static host |
| Needs an account | Yes | No |
| What you deploy | Nothing | The `dist/` folder |

## Publish

```sh theme={"dark"}
interactive-demo login      # once per machine
interactive-demo publish    # prints the demo's URL
```

There is nothing to deploy and nothing to configure. `publish` uploads the demo, prints its URL, and prints the sized iframe snippet for it.

**Publishing again updates the same URL.** Embeds you have already pasted keep working and pick up the new version. `--new` mints a separate URL and leaves the old one serving the old demo.

**Your files do not change.** `publish` uploads each file the config references and rewrites those paths to hosted URLs in the copy it sends. The paths in your `demo.config.json` stay relative. The one exception is a demo with no `id`: `publish` writes a new one to the file first, because the hosted URL is keyed on it. Commit it.

With more than one demo in the project, name the one to publish:

```sh theme={"dark"}
interactive-demo publish demos/onboarding
interactive-demo publish --demo onboarding
interactive-demo publish --list    # every demo, with its URL or "(not published)"
```

<Warning>
  `publish` does not upload a project-relative `brand.logo`. It drops the logo from the hosted page bar and warns. Use an absolute `https://` URL in `interactive-demo.json` for a logo that should appear there.
</Warning>

For CI, `login --token <token>` or the `INTERACTIVE_DEMO_API_TOKEN` environment variable skips the browser. See [`login`](/open-source/cli#login) and [`publish`](/open-source/cli#publish) in the CLI reference.

## Host it yourself

```sh theme={"dark"}
interactive-demo build
```

`build` writes one self-contained folder per demo:

```text theme={"dark"}
dist/<slug>/
  index.html         the page: stylesheet, the config script tag, root, player.js
  player.js          the player, with React bundled in
  player.css         the stylesheet
  player-fonts.css   optional: fonts/*.woff2 and backgrounds/ beside it
  assets/…           screenshots, recordings, audio
  brand/…            the project logo, when brand.logo is a project file
dist/embed.js        the pop-up loader, once for the whole folder
```

Nothing in the folder depends on where it is served from. Every URL inside it is relative to the page. Move the folder, rename it, or nest it under any path, and it still works. Nothing in it phones home.

Any static host works: GitHub Pages, Netlify, Vercel, Cloudflare Pages, an S3 bucket behind a CDN, or a folder on your existing web server. Upload `dist/`, or just one `dist/<slug>/`, and note the URL of `index.html`.

`build` also prints the inline iframe and pop-up snippets for the first demo, with a placeholder host to replace with wherever you deploy.

Three things to know:

* **Run `validate` first.** `build` copies `assets/` as-is. A media path with no file behind it is not caught, and the page 404s at runtime.
* **Cache `player.js` long and `index.html` short.** The page embeds the demo config, so a rebuilt demo changes `index.html`. The player only changes when you upgrade the runtime.
* **Recordings can be large.** If your host has a file-size limit, keep video steps short. Capture already trims them to the motion.

<Warning>
  `build` empties its output folder first. It only empties a folder that is missing, empty, or one it created. Any other non-empty folder is refused unless you pass `--force`.
</Warning>

`player-fonts.css`, `fonts/`, and `backgrounds/` are optional. Delete them from a built folder and the player falls back to system fonts and a flat cover background.

### Assemble the page yourself

A built page is the player plus a title bar. The player itself needs only this:

```html theme={"dark"}
<link rel="stylesheet" href="./player.css">
<link rel="stylesheet" href="./player-fonts.css">  <!-- optional; ./fonts/ next to it -->
<script id="demo-config" type="application/json">{ …demo.config.json… }</script>
<div id="root"></div>
<script src="./player.js"></script>
```

Media paths in the config resolve relative to the page, so keep the page in the demo folder. See [The static page contract](/open-source/runtime#the-static-page-contract).

## Next

<CardGroup cols={2}>
  <Card title="Sharing and embedding" icon="code" href="/open-source/embedding">
    Send the link, frame the page, or render it in React.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/open-source/cli">
    Every flag for `publish`, `build`, `embed`, and `login`.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.