Creator guide · 1 of 3

Build a VibeFuse widget — from empty folder to marketplace listing

A VibeFuse widget is a tiny web app: a folder with manifest.json and index.html. VibeFuse serves it over the widget:// protocol inside a sandboxed iframe on the canvas, next to your terminals and agents. This guide walks through the whole loop — write the files, run it on your own canvas, publish it from Settings → Marketplace, polish the listing, and price it if you want to sell.

No build step HTML + CSS + JS iframe runtime Free or paid 80% seller payout

What you are building

By the end you will have a working Quote Board widget — a rotating quote card with a shuffle button and favorites that persist between sessions — installed on your canvas and listed on the marketplace. Swap the content for anything you like; the packaging, testing, and publishing steps are identical for every widget.

Format
Folder with manifest.json + entry HTML
Runtime
Sandboxed iframe over widget://
Where it lives
C:\vibefuse\widgets
Time to first run
~10 minutes

What a marketplace widget looks like on the canvas

Prerequisites

  • VibeFuse for Windows installed and signed in with your complimentary VF- key (Account → Product Keys).
  • Any text editor. No Node, bundler, or framework is required — a widget is static HTML.
  • Basic HTML/CSS/JavaScript. If you can build a web page, you can build a widget.
  • To sell (optional): a finished seller profile and Stripe Connect onboarding in Account → Stripe Connect.
Prefer to let the agent do it? Open a Vibe Agent widget and say “create a widget folder called quote-board in my widgets directory with a manifest and index.html”. It writes the same files this guide shows — you still review, test, and publish.

Package anatomy

Only two files are mandatory. Everything else is optional and referenced relatively from your HTML.

RuleDetail
Allowed file typeshtml, htm, css, js, mjs, json, svg, md, txt, map
Size limits750 KB per file · ~2.5 MB per package (text only)
Binary assetsNot uploaded. Inline SVG, use CSS gradients, data-URIs for tiny images, or load from a URL you host
PathsRelative to the package root. .. and .git/ paths are dropped

Step 1 — manifest.json

The manifest tells VibeFuse what the package is, how big it should spawn, and what it needs. Copy this and change the id, name, and description.

manifest.json json
{
  "id": "com.yourname.quote-board",
  "name": "Quote Board",
  "version": "1.0.0",
  "description": "Rotating motivational quotes with a one-click shuffle. Remembers your favorites.",
  "author": "Your Name",
  "license": "MIT",
  "icon": "sparkles",
  "category": "utility",
  "kind": "marketplace",
  "runtime": "iframe",
  "entry": "index.html",
  "permissions": ["storage"],
  "defaultSize": { "w": 4, "h": 5 },
  "minSize": { "w": 3, "h": 4 },
  "tags": ["quotes", "focus", "productivity"]
}
FieldRequiredWhat it does
idYesGlobally unique, reverse-DNS style. 3–128 chars of letters, numbers, . _ -. Republishing with the same id updates the listing in place — never change it after launch.
nameYesDisplay name on the rail, catalog cards, and the listing page.
versionNoSemver string. Defaults to 1.0.0. Bump it every time you republish.
descriptionNoOne or two sentences. Shown on cards; falls back to name.
authorNoOverwritten with your profile name when you publish.
licenseNoFree text, e.g. MIT. Shown to buyers.
iconNoIcon key for the rail (sparkles, hexagon, clock, terminal …). Defaults to sparkles.
categoryNoOne of cli, browser, utility, media, custom. Anything else becomes utility.
kind / runtimeNoAlways normalized to marketplace / iframe on publish.
entryNoThe HTML file to load. Defaults to index.html; the file must exist in the package.
permissionsNoArray of capabilities the widget declares. Defaults to ["storage"]. See Permissions.
defaultSizeNoGrid units { "w", "h" } used when the widget first spawns. Default 4 × 6.
minSizeNoSmallest size a user can drag it to. Default 3 × 4.
tagsNoSearch keywords. marketplace is added automatically.
Pick the id carefully. It is the identity the marketplace uses to match your future updates to this listing, and an id already claimed by another creator is rejected at publish time.

Step 2 — index.html starter

A complete, working widget. It is a normal HTML document: one file, inline styles and script, no dependencies. Notice the dark background — widgets sit on a dark canvas, so design for it.

index.html html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Quote Board</title>
<style>
  * { box-sizing: border-box; }
  html, body {
    height: 100%; margin: 0;
    background: linear-gradient(165deg, #0a1018, #06080d);
    color: #eef2f8;
    font-family: Inter, system-ui, sans-serif;
    display: grid; place-items: center;
  }
  .card {
    width: min(92%, 420px);
    padding: 22px 24px;
    border-radius: 18px;
    border: 1px solid rgba(201, 169, 98, 0.22);
    background: rgba(10, 16, 26, 0.72);
    text-align: center;
  }
  .kicker { font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; opacity: 0.5; }
  blockquote { margin: 12px 0 6px; font-size: 18px; line-height: 1.4; min-height: 3.2em; }
  cite { display: block; font-style: normal; font-size: 12px; color: #c9a962; }
  .row { display: flex; gap: 8px; justify-content: center; margin-top: 16px; }
  button {
    border: 1px solid rgba(201, 169, 98, 0.35);
    background: rgba(201, 169, 98, 0.12);
    color: inherit; border-radius: 999px; padding: 8px 16px;
    font: inherit; font-size: 13px; cursor: pointer;
  }
  button:hover { background: rgba(201, 169, 98, 0.22); }
  button.is-on { background: #c9a962; color: #14110a; }
  .count { margin-top: 10px; font-size: 11px; opacity: 0.55; }
</style>
</head>
<body>
  <div class="card">
    <div class="kicker">Quote board</div>
    <blockquote id="q"></blockquote>
    <cite id="a"></cite>
    <div class="row">
      <button id="next">Shuffle</button>
      <button id="fav">♥ Favorite</button>
    </div>
    <div class="count"><span id="n">0</span> favorites saved</div>
  </div>

<script>
  // Widgets are plain web pages inside a sandboxed iframe.
  // Use standard browser APIs — no framework or build step required.
  const quotes = [
    ["Simplicity is the soul of efficiency.", "Austin Freeman"],
    ["Make it work, make it right, make it fast.", "Kent Beck"],
    ["Programs must be written for people to read.", "Harold Abelson"],
    ["The best way to predict the future is to invent it.", "Alan Kay"],
    ["Deleted code is debugged code.", "Jeff Sickel"]
  ];

  // "storage" permission → localStorage persists between canvas sessions.
  const KEY = "quote-board:favorites";
  const favorites = new Set(JSON.parse(localStorage.getItem(KEY) || "[]"));

  const q = document.getElementById("q");
  const a = document.getElementById("a");
  const n = document.getElementById("n");
  const fav = document.getElementById("fav");
  let index = Math.floor(Math.random() * quotes.length);

  function render() {
    const [text, author] = quotes[index];
    q.textContent = "“" + text + "”";
    a.textContent = "— " + author;
    fav.classList.toggle("is-on", favorites.has(index));
    n.textContent = String(favorites.size);
  }

  document.getElementById("next").onclick = () => {
    index = (index + 1 + Math.floor(Math.random() * (quotes.length - 1))) % quotes.length;
    render();
  };
  fav.onclick = () => {
    favorites.has(index) ? favorites.delete(index) : favorites.add(index);
    localStorage.setItem(KEY, JSON.stringify([...favorites]));
    render();
  };

  render();
</script>
</body>
</html>

Every line above is standard browser code. There is no VibeFuse SDK to import: the host provides the window, the sandbox, and persistence; your page provides the experience.

Live example — the exact code above, running

This is the index.html from Step 2 rendered in a sandboxed iframe, the same way VibeFuse renders it on the canvas. Click Shuffle and Favorite; reload the page and the favorites count survives because the widget uses localStorage.

Quote Board — widget://quote-board/index.html live · runs in your browser

On the desktop this window has the standard VibeFuse widget chrome: title, fill-view, and close. The iframe content is yours.

Two more shipped widgets you can read as reference: Focus Timer (interval timer with cycles) and Local Time Clock (ticking clock with timezone). Both are under 60 lines.

Step 3 — Saving state between sessions

Declare "storage" in permissions (the default) and use localStorage or IndexedDB. Storage is scoped to your widget origin, so namespacing keys keeps things tidy when you ship a second widget.

app.js — persistence pattern js
const KEY = "quote-board:settings";

// Read once on boot, with a safe fallback.
const settings = Object.assign(
  { theme: "gold", interval: 30 },
  JSON.parse(localStorage.getItem(KEY) || "{}")
);

// Write whenever something changes.
function save(patch) {
  Object.assign(settings, patch);
  localStorage.setItem(KEY, JSON.stringify(settings));
}

save({ interval: 45 });
  • Each canvas instance of your widget shares the same origin storage — if you need per-instance state, key it by a random id you generate on first run and keep in sessionStorage.
  • Keep payloads small (kilobytes, not megabytes). Widgets boot every time a session opens.
  • Never store secrets. Widgets are plain files a user can open in Notepad.

Step 4 — Design for the canvas

Widgets are resized freely by the user and sit beside terminals, browsers, and agent chats. A few habits make yours feel native:

Layout

  • Fill the viewport: html, body { height: 100%; margin: 0 }
  • Center a single card with display: grid; place-items: center
  • Use min() / clamp() so text scales from 3×4 to full-screen
  • Respect minSize — test at the smallest size you allow

Look & feel

  • Dark base (#06080d#0a1018) with one accent — gold #c9a962 and blue #8fb0d4 match the shell
  • Translucent panels: rgba(10,16,26,.72) + 1px border at 20% alpha
  • System font stack (Inter, system-ui, sans-serif) — no web-font downloads
  • Pill buttons, 999px radius, hover state only (no heavy animations)

The canvas itself is themed by the user's Appearance settings or an installed styling pack; your widget's interior is fully yours. Neutral dark surfaces look right under every theme.

Widget window chrome VibeFuse draws around your iframe

Permissions & limits

Permissions are declared in the manifest so users and reviewers can see what a widget intends to do. Request the minimum.

PermissionUse it when
storageYou persist anything with localStorage / IndexedDB. Default.
networkYou call external HTTP APIs (weather, crypto prices, your own backend). Use HTTPS and handle offline.
clipboardYou read or write the clipboard (copy buttons).
webviewYou embed third-party pages in an inner iframe.
voiceYou integrate with the voice dock / TTS surface.
pty, shell, fs.read, fs.writeReserved for privileged built-in widgets. Marketplace iframes run in a sandbox and do not get shell or file-system access — build those workflows as skills for the agent instead.
  • Widgets cannot reach other widgets, the terminal, or the file system. They are isolated web pages.
  • Anything with obfuscated code, crypto-mining, or hidden network calls is removed from the marketplace and the seller account can lose listing rights.

Step 5 — Test it on your own canvas

  1. 01
    Drop the folder into your widgets directory

    Copy quote-board\ into C:\vibefuse\widgets (the path is shown in Settings → Widgets and Settings → Marketplace; use Reveal folder to open it).

  2. 02
    Rescan the catalog

    Reopen Settings, or restart VibeFuse. The widget appears in Settings → Widgets with a Local badge and under Widgets in the left rail.

  3. 03
    Spawn it

    Click it in the rail (or ask Jarvis: “open the quote board widget”). It spawns at your defaultSize. Resize down to minSize and up to fill-view to check the layout.

  4. 04
    Iterate

    Edit index.html, then close and reopen the widget to reload the iframe. Right-click → Reload also works on most builds.

  5. 05
    Check persistence

    Favorite a quote, close the widget, reopen — the count should survive. Close the whole session and reopen to confirm it survives restarts too.

Settings → Widgets: built-in + local + marketplace catalog

Widget cards with Local / Marketplace badges

Step 6 — Publish from Settings → Marketplace

Publishing uploads the text files in your folder and creates (or updates) a marketplace listing tied to your account. Your profile name is stamped as author.

  1. 01
    Confirm your license is linked

    The top of Settings → Marketplace shows your seller profile when a valid VF- key is active. If not, grab yours from Account → Product Keys.

  2. 02
    Find the widget under Local widgets

    Each folder in C:\vibefuse\widgets shows as a card with a status: Not on marketplace, Uploaded · unpublished, or Live on marketplace. Use Choose other folder to publish from anywhere else on disk.

  3. 03
    Click “List on marketplace”

    VibeFuse validates the manifest (id, name, entry file present, allowed file types, size) and uploads. Errors are shown inline — fix the file and click again.

  4. 04
    Publish

    New uploads may land as unpublished. Click Publish on the card, or Publish all (N) in the banner. The card flips to Live on marketplace with a View link.

Settings → Marketplace: seller profile, local widgets, listings

Local package cards: Not on marketplace → Live on marketplace

Republishing the same folder updates the existing listing (matched by manifest id). Publishing a folder whose id or slug belongs to another creator is rejected.

Step 7 — Polish the listing on the web

Uploads carry code; the storefront carries the pitch. Open Account → Marketplace to finish the public page:

FieldRecommendation
Card imageSquare-ish, dark background, the widget at its default size. Shown on catalog cards and the trending slider.
BannerWide (roughly 3:1). The detail page hero fades it under the title — keep the focal point centered.
Screenshots + captions3–5 shots: default size, resized small, a state that shows value (favorites, settings). One-line captions.
Long descriptionWhat it does, how to use it, what it stores, what it calls over the network. Rich text is supported.
Published / UnpublishedUnpublish hides the page without deleting the package or losing saves.

Your listing page lives at /marketplace/widget/<slug> and shows version, category, saves, views, a screenshot slider, your creator card, and Save to VibeFuse. Followers of your storefront see new drops on the marketplace pulse.

The in-app marketplace where buyers find your widget

Step 8 — Free or paid

Free ($0.00)

Anyone signed in can Save to VibeFuse; the package syncs into their C:\vibefuse\widgets on next launch. Best for building followers and reputation.

Paid ($0.50 – $500.00)

Requires Stripe Connect fully enabled. Buyers pay through hosted Stripe Checkout; you keep 80%, Fuse Intelligence takes a 20% platform fee at checkout. Earnings show on Account Overview and in Stripe.

  • Set the USD price in Account → Marketplace; two decimals, min $0.50, max $500.
  • You never invoice buyers yourself — Stripe handles receipts, tax lines, and payouts.
  • A paid widget plus a matching skill and styling pack is an easier sale than three separate items.

Step 9 — Ship an update

  1. 01
    Edit the files

    Change index.html, add files, whatever you need. Keep the manifest id identical.

  2. 02
    Bump the version

    "version": "1.1.0". Buyers see the version on the listing page; it also helps you read support questions.

  3. 03
    Update listing

    In Settings → Marketplace the card now reads Live on marketplace with an Update listing button. Click it — files and metadata are replaced in place; saves, views, and price are kept.

  4. 04
    Users get it on next sync

    Library sync runs at VibeFuse launch and when the in-app marketplace closes, so installs pick up the new package automatically.

Pre-publish checklist

  • manifest.json has a unique reverse-DNS id, a clear name, and a one-sentence description
  • entry file exists and opens correctly when double-clicked in a browser
  • Only allowed text file types; no file over 750 KB; nothing loaded from node_modules
  • Looks right at minSize, defaultSize, and fill-view
  • Dark background, no white flash on load, no web-font requests
  • permissions lists exactly what you use; network calls are HTTPS and documented in the description
  • No secrets, analytics beacons, or obfuscated code
  • Card image, banner, 3+ screenshots with captions, long description written
  • Price decided; Stripe Connect enabled if paid

Troubleshooting

SymptomFix
Widget does not appear in Settings → WidgetsFolder must be directly inside C:\vibefuse\widgets and contain a valid manifest.json. Check for trailing commas — JSON, not JS. Reopen Settings to rescan.
Blank white or black iframeentry points to a file that is missing or misnamed. Open DevTools on the widget (right-click → Inspect) and read the console.
“manifest.json must include id and name”Both fields are required strings. The id must match ^[a-z0-9][a-z0-9._-]{2,127}$.
“Unsupported file type”Remove images/fonts/binaries from the folder or move them to a URL you host. Only text types upload.
“already listed by another creator”Change your id — someone else owns that one. Use your own namespace: com.yourname.*.
Update listing did nothingBump version and make sure you published from the same folder / same id. Check the tab's sync log.
State resets every timeConfirm "storage" is in permissions and you are writing to localStorage, not a variable.

FAQ

Can I use React, Vue, or Tailwind?

Yes, as long as the output is static text files. Build locally, then publish the dist/ folder (use Choose other folder). Keep bundles under the 750 KB per-file limit and avoid loading from a CDN unless you declare network.

Can a widget talk to the terminal or my files?

No — marketplace widgets are sandboxed iframes. For workflows that need the shell, files, or MCP tools, write a skill; the Vibe Agent runs it with full tool access.

Can I call my own API?

Yes. Declare network, use HTTPS, send CORS headers from your server, and degrade gracefully when offline. Say what you call in the listing description.

How do I include images or a logo?

Inline SVG in the HTML, CSS gradients, or small data-URI PNGs. Binary files are not part of the upload. Listing artwork (card, banner, screenshots) is uploaded separately in Account → Marketplace.

Do I need to ship Windows binaries or an installer?

No. VibeFuse is the runtime. Your package is text files that VibeFuse serves over widget://.

Can I unpublish or delete a widget?

Unpublish from Settings → Marketplace or Account → Marketplace hides the listing and stops new saves; existing installs keep working. Contact support to remove a listing entirely.

Next guides

Related: Widget types · Sell & earn · Marketplace · How to sell AI widgets