Skip to content
HotLayout Docs

Live preview on a phone

On my phone in the editor shows the person's draft on their own phone: they scan a QR code, and every change the editor saves reaches the phone within a few seconds. This page describes the file the phone reads, so that a native reader (the HotLayout Preview app, or your own debug build on top of an SDK) can show the same draft.

Nothing here is part of what your app reads in production. A live copy is a working tool of the person who edits; your app keeps reading the published document as described in Delivery.

Where the copy lives#

With the HotLayout cloud the copy is one file in storage:

orgs/{orgId}/projects/{projectId}/documents/{documentId}/live/{token}.json
  • token is 32 random bytes in base64url without padding: 43 characters of A–Z a–z 0–9 - _. It is the only secret; the folder cannot be listed.
  • Anyone with the address can read the file, without signing in.
  • The file is readable for one hour after its last write. Every saved change rewrites it, so the copy stays alive while the person works and dies on its own when the tab is forgotten.
  • Only a member who may edit the document can write it.

The QR code holds a page address, not the file address:

https://hotlayout.com/live#orgs/{orgId}/projects/{projectId}/documents/{documentId}/live/{token}.json

The part after # is the storage path of the file. It is never sent to a server. The page reads the file and draws it with the web renderer; it links to the app with:

hotlayout-preview://live?u={file address, percent-encoded}

where the file address is the HTTPS address of the file itself (for the cloud, the storage download address with alt=media). A reader that gets the page address can turn it into the file address the same way.

The envelope#

The file is UTF-8 JSON, at most 1 MiB, in one of two forms.

A copy that is being shown:

{
  "format": "hotlayout-live/1",
  "savedAt": "2026-10-26T10:00:05.123Z",
  "document": { "documentId": "…", "screens": [] },
  "theme": { "defaultThemeId": "light", "themes": {} },
  "dataContract": null
}
Field What it is
format Always hotlayout-live/1. A reader that sees another value treats the file as unreadable and does not guess.
savedAt When the editor wrote this copy, ISO 8601 by the editor's clock. Compare it as a string to tell that the copy changed.
document The draft document, the same shape as a published document. Read it the way you read published.json, including migration of older documents.
theme The whole theme contract, in the shape of the published theme: defaultThemeId and themes. Draw with themes[defaultThemeId]. null — use your built-in theme.
dataContract The data dictionary of the project, with its samples, or null for the default dictionary. Draw bindings with its samples: a live copy has no backend behind it.

A stopped show:

{ "format": "hotlayout-live/1", "stopped": true }

The editor never deletes the file. Stop showing rewrites it with this form, so a reader learns at once that the show is over.

Reading it#

  • Poll every two seconds with a plain GET. Show the new copy when savedAt differs from the one on screen; keep the screen the person is on (the stack of screens) when only the content changed.
  • 403 or 404 — the copy has expired or never existed: say "this link has expired" and stop polling.
  • { "stopped": true } — say "showing has been stopped" and stop polling.
  • A network error after a copy was shown — keep showing it and say that there is no connection; poll again.
  • From a browser page on another origin, do not send If-None-Match: it turns the request into one with a preflight, which the cloud storage does not allow for this file. Native readers may use ETag and If-None-Match freely.

Self-hosted storage#

With self-hosted storage the editor writes the same envelope to your API with PUT documents/{id}/live/{token}. The method is optional: without it the editor has no On my phone button. The answer may name the address where the phone reads the copy without signing in ({ "url": "https://…" }); without it the editor uses the address of the request. The hotlayout.com/live page reads only the HotLayout cloud, so with your own storage the QR code holds the app link directly.