Skip to content

Selfhosting

KabanFriends edited this page Aug 27, 2026 · 3 revisions

Everything mcwidgets serves is static. If you can host a directory of files, you can host the widgets, and your embeds stop depending on someone else's uptime, version choices, and traffic logs.

The repository has one command that assembles the whole site, and the same command backs the container image and the GitHub Pages deployment. There is no second definition of the layout to keep in sync.

Build the site

git clone https://github.com/KabanFriends/mcwidgets.git
cd mcwidgets
npm ci
npm run build
npm run stage

You need Node 20.19 or later, or Node 22.12 or later.

npm run build compiles the widget modules and the preprocessors. npm run stage assembles them into .host/, which is the site. Copy that directory to any static web server and you are done.

npm run stage deletes .host/ before writing it. Add your own files after staging, not before.

What the site contains

.host/
  index.html                     a landing page with both widgets embedded
  advancements/
    embed.html                   iframe shell
    viewer.js                    ES module
    assets/                      window, tab, frame, and background textures
  itemgui/
    embed.html                   iframe shell
    itemgui.js                   ES module
    assets/                      slot highlight and tooltip textures
  core/                          one shared copy of the font and tooltip sprites
  bundles/                       demo bundles, if you built them

Each widget directory is self-contained. viewer.js finds its textures and its font in the assets/ beside it, which is why an embed needs no assetBase. Keep those two together when you move things around, and core/ stays available for hosts that would rather point every widget at one shared copy with coreAssetBase.

Every URL inside the tree is relative. The site is correct at a domain root and under a subdirectory alike, so https://widgets.example.com/ and https://example.com/mcwidgets/ both work with no configuration.

Add your own bundles

The bundles/ directory holds the demo content and is optional. Build your own with the preprocessors and drop them anywhere the tree can reach:

npm run stage
node packages/advancements/bin/mcw-adv-preprocess.mjs \
  --input ~/server/datapacks/my_pack \
  --output .host/bundles/my-pack

Then embed them, with the bundle named relative to the shell:

<iframe
  src="https://widgets.example.com/advancements/embed.html?bundle=../bundles/my-pack/bundle.json"
  style="width: 100%; height: 420px; border: 0;"
></iframe>

bundle resolves against embed.html, and the textures and the font resolve against viewer.js beside it, so everything comes from your own host and the whole embed is same-origin with the shell. Cross-origin headers never enter into it. That is the main practical reason to self-host.

See Building advancement bundles and Building item GUI bundles.

Building the demo bundles

The staged tree includes bundles/ only if the demo bundles exist. To build them, which downloads the vanilla advancement JSON and every icon it references:

npm run fetch-demo-data -w @mcwidgets/advancements
npm run preprocess:vanilla -w @mcwidgets/advancements
npm run preprocess:demo -w @mcwidgets/itemgui
npm run stage

fetch-demo-data lists files through the GitHub API, which allows 60 unauthenticated requests an hour. Set GITHUB_TOKEN if you hit the limit.

Serve it locally

npm run host

This stages the site and serves it on port 8080, binding all interfaces so another machine on the network can open it. The startup log prints the URLs, including one per network interface. Pass a different port with npm run host -- --port 3000.

Use it for development. It sends Cache-Control: no-cache and no cross-origin headers at all, so it is not a production server.

What a production server needs

Serve .host/ as-is. Four things matter.

Cross-origin headers. Everything served is public static content, and the whole point is that someone else's page loads it. Both import of a cross-origin ES module and fetch of a bundle.json need Access-Control-Allow-Origin. Send *.

add_header Access-Control-Allow-Origin "*" always;

Neither request is preflighted, so there is no OPTIONS handler to write.

Cross-Origin-Resource-Policy. A page that turns on COEP blocks these subresources even when CORS allows them. Send cross-origin so those embedders work.

add_header Cross-Origin-Resource-Policy "cross-origin" always;

Font MIME types. The default pixel font is a .ttf. Some servers do not know the type and send application/octet-stream, which browsers refuse to use as a font.

Compression. bundle.json and the widget modules compress well. A vanilla advancement bundle is mostly repeated keys. PNG and TTF are already compressed, so leave them alone.

The repository ships an nginx configuration at docker/default.conf that does all four, plus a /healthz probe endpoint. Read it even if you use a different server, since it is short and it is the reference for what the widgets expect.

Container image

The repository includes a Dockerfile that copies the staged tree into nginx with that configuration.

npm run build
npm run stage
docker build -f docker/Dockerfile -t mcwidgets .
docker run -p 8080:80 mcwidgets

.dockerignore narrows the build context to .host/ and docker/, so the image is the site and nothing else.

Prebuilt images are published to ghcr.io/kabanfriends/mcwidgets, tagged by branch and build time. Pick a tag explicitly. There is no moving tag to follow.

GitHub Pages

The repository includes a workflow that builds the site, builds the demo bundles, stages, and deploys to GitHub Pages on every push to master. To use it in your fork, set Source to GitHub Actions in the repository's Pages settings.

A project site is served from /<repo>/ rather than a domain root. Nothing in the tree cares, because every URL in it is relative.

Pinning a Minecraft version

packages/mcassets/mc-version.json names the Minecraft version that every build-time download and every bundle's metadata is pinned to. Editing that one file moves both. It is what makes two runs of the same input produce the same output.

Attribution

Minecraft textures and fonts are © Mojang Studios. Self-hosting means redistributing them, so follow Mojang's usage guidelines.

Clone this wiki locally