Skip to content

Developing for Stackboard

Stackboard reads an entry document from your repository: index.html at the root by default, or any path you set. It inlines the local stylesheets and scripts that document names, rewrites the other asset URLs, and serves the result.

It does not run a build. Whatever is committed is what runs, so if your project has a build step, commit its output.

The simplest Deployable is three files at the root:

index.html
styles.css
app.js

index.html links the other two the ordinary way, and Stackboard follows those links.

With a build step, commit the output folder and point the entry path at its index.html:

src/ source, ignored by Stackboard
dist/
index.html entry path: dist/index.html
assets/
app.js
app.css

The entry document’s folder is the site root. In dist/index.html, a reference to /assets/app.js means dist/assets/app.js, not the repository root. That’s what makes built output work unchanged.

Anything with a static adapter works, as long as the build output is committed:

Framework Setting Output
Vite default dist/
Astro default dist/
Create React App default build/
Next.js output: 'export' out/
SvelteKit adapter-static build/

Frameworks needing a Node server at runtime won’t run, since there is no server to run them on.

Plain HTML with a script tag is a perfectly good Deployable. Don’t add a build step you don’t need, because every one of them is something to remember to re-run before committing.

Relative and root-relative paths resolve inside the repository. Absolute URLs are left alone, so libraries and fonts from a CDN load normally.

The entry document and the files it pulls in have a 20 MB budget, applied to each file and to the total. That is generous for hand-written code and easy to exceed by committing a large media file.

Write %VAR% anywhere in your source and Stackboard substitutes the stored value at launch:

const region = '%DEFAULT_REGION%';

A placeholder with no stored value is left as the literal text %VAR%, so a typo shows up on the page rather than silently becoming an empty string.

Substitution applies to public variables only. See Secrets.

Secrets are stored in Stackboard, never in the repository, and never reach the browser. Your code cannot read one, but it can use one.

Configure an API target: a name, an origin, which secret to attach, and whether it goes in a header or a query parameter. Restrict the methods and paths it accepts, since a target otherwise grants the whole origin.

Then call the API normally. Requests to a configured target’s origin are rerouted through Stackboard’s proxy, which attaches the credential after the request leaves the browser:

const res = await fetch('https://api.example.com/v1/items');

stackboard.fetch(target, path) does the same explicitly. Blocked calls are logged with a reason, so a too-narrow allowlist is easy to tell from code asking for something new.

Declare a variable without a value and each person supplies their own before the Deployable launches. Those are visible to nobody else, including you.

A secret written as %VAR% is not substituted. Stackboard leaves it as the literal placeholder on purpose. If a key is missing at runtime, that’s why: the code is trying to inline something it must reach through a target instead.

Local development needs its own arrangement. Use a .env file, a dev fallback, or a local proxy, and replace it with %VAR% and an API target for Stackboard. Never commit the .env.

Only the proxy is reachable. The page runs sandboxed, and network access is restricted to Stackboard’s origin. A fetch to an API with no configured target fails, however permissive that API’s CORS headers are.

Browser storage is unavailable. The sandbox gives the page an opaque origin, so localStorage, sessionStorage, and cookies don’t work.

Stale build output. Since Stackboard runs what’s committed, forgetting to rebuild before committing ships the previous version. Building in CI and committing the result avoids this.

A path that’s right locally can be wrong here. Check that your entry document’s links work when its own folder is the site root.