Live code playgrounds
A Code demo (the playground block) puts runnable code on a page: editors for the source beside a live preview, a Run button, a Reset button, and the console output under it. Readers can change the code in their own copy and run it again; your version is never touched. It is the block to use for "try this easing curve", "here is the component, change the props", or a CSS trick a picture cannot show.
Playgrounds come in two runtimes:
| What the JS editor holds | What runs |
|---|---|---|
unset (or | Plain JavaScript, beside HTML and CSS | Your HTML, CSS and script, as a small web page |
| JSX or TypeScript (the editor tab reads JSX) | Your code compiled in the sandbox and run with React 19; its default export is mounted as the page's root |
Before you build on this: demos only run once Forumulate has configured the sandbox domain they run on. Until then readers see the block's fallback picture. See What you need to set up.
Adding one
In the page editor, type / and choose Code demo (under Advanced). The block's settings panel, Code and fallback, holds:
Title — a heading over the demo ("Easing, side by side").
Runs as — HTML, CSS and JavaScript, or React (JSX, default export is mounted).
The HTML, CSS and JavaScript (or JSX) sources, up to 20,000 characters each.
Layout — Side by side, Stacked or Preview only.
Readers may edit the code — off makes the editors read-only and removes Reset.
Preview height (px) — 120 to 1200, default 320.
Fallback picture readers see and Fallback picture described — the picture shown while the demo loads, and instead of the demo when demos cannot run.
The attributes, the JSON form and the markdown form are listed in the authoring reference under playground.
Plain HTML, CSS and JavaScript
:::playground{title="Easing" layout="split" fallback="/api/assets/p1" fallbackAlt="Two boxes, one easing out"}
```html
<div class="box"></div>
```
```css
.box { width: 48px; height: 48px; background: #6d28d9; transition: transform .3s ease-out }
.box:hover { transform: translateX(120px) }
```
```js
console.log('ready')
```
:::The sources are ordinary fenced code blocks tagged html, css and js (the JSX of a React demo is tagged js too). Only the sources you set get an editor. noedit makes them read-only; tabs="js,html" orders the editors.
React components (runtime: "react")
With runtime="react" the JavaScript editor holds a module in JSX or TypeScript. It is compiled inside the sandbox (TypeScript types and JSX are stripped and the module is turned into plain JavaScript there, in the reader's browser) and then run. If the module has a default export, it is rendered as a React component into #root.
:::playground{title="A counter" runtime="react" fallback="/api/assets/p2" fallbackAlt="A button reading Clicked 3 times"}
```js
import { useState } from 'react';
export default function Counter({ start = 0 }: { start?: number }) {
const [n, setN] = useState(start);
return <button onClick={() => setN(n + 1)}>Clicked {n} times</button>;
}
```
```css
button { font: 600 16px system-ui; padding: 8px 14px; border-radius: 8px }
```
:::The rules of the React runtime:
Topic | Rule |
|---|---|
Imports |
|
Mounting | The default export is rendered into the element with |
No default export | The module still runs; nothing is mounted. Useful for a script that draws into your own HTML. |
JSX | The automatic JSX runtime: you do not need |
Errors | A compile error, or the React runtime failing to load, is printed in the demo's console under the preview, so a reader sees why nothing rendered. |
CSS | The CSS editor works as it does in a plain demo, and styles the whole demo page. |
Where the code runs, and why
A playground runs code you (or any editor of the space) wrote, and code a reader typed. Neither may ever run with a reader's session. So demo code never runs on your site's address, on a custom domain, or on forumulate.com:
It runs in an
<iframe sandbox="allow-scripts">loaded from a separate sandbox domain — a different registrable domain from ours, so no cookie of ours can reach it.The demo page's own policy allows inline code and drawing, and forbids everything else: no
fetch, no websockets, no beacons, no form posts, no navigation. Images fromhttps:addresses, anddata:andblob:media, still load.The React runtime itself is served from that same sandbox domain and nowhere else; on any other address its file is a 404.
The demo starts when it scrolls into view, and the preview's height is reserved before it loads, so the page does not move.
What you need to set up
Nothing on your side. Playgrounds need a sandbox domain on Forumulate's side: a separate registrable domain pointed at our reader service. A subdomain of ours is refused on purpose, because it would share cookies with the reader; the feature stays off rather than run unsafely.
Until Forumulate has configured it:
Your code is saved with the page (both runtimes), so every playground you write starts working the day the sandbox domain does, with no change on your side.
In the editor, the block says "Interactive demos need a sandbox domain — not yet available", and explains that readers see the fallback picture.
Readers see only the fallback picture (with its caption), or nothing at all when the block has none. The code is never put into the page, because a page that cannot run it should not show a dead editor.
So give every playground a fallback picture: a screenshot of the demo's end state is enough. It is also the poster readers see for the moment before a running demo loads.
Once the sandbox is live, the editor's note changes to "Readers can run and edit this demo", and readers get the editors, the preview, Run, Reset and the console.
Styling
The block carries data-fx-playground, so customCss and elementStyles can reach it (the fallback figure too). Inside a running demo, only the demo's own CSS applies: the sandbox page does not load your theme.
Related
Authoring reference — the
playgroundblock's attributes.Markdown directives —
:::playgroundin markdown.Theme a documentation space — the API reference's "Try it" console, which is a different feature (it calls your API, not a sandbox).