What is a BApp?
A BApp (Browser App) is a self-contained React component that lets a user generate some content — an image, SVG, HTML page, text, JSON, or any other inscription-ready format — and then inscribe it onto Bitcoin with one click.
You write the creative UI. Our BAppRuntime wraps it with:
- ◈A live content preview (image, HTML iframe, SVG, formatted JSON, etc.)
- ◈An Advanced Options panel (parent IDs, delegate, metadata, postage)
- ◈An Inscribe button that converts your output → NexusInscriberJson → Oodinals
- ◈Developer fee injection if your BApp definition specifies one
core rule
Your BApp calls onOutput() whenever the user creates or changes content. That's your only responsibility. Everything else — preview, advanced options, base64 encoding, payload building, inscription routing — is handled by the platform.
The output contract
When the user interacts with your BApp and produces content, call onOutput(output). The shape of BAppOutput is:
interface BAppOutput {
// Required: the actual content to inscribe
content: string | Blob; // string for HTML/SVG/text/JSON; Blob for binary
// Required: MIME type
contentType: string; // e.g. 'image/svg+xml', 'text/html;charset=utf-8'
// Optional but recommended
fileName?: string; // e.g. 'my-art.svg'
title?: string; // shown in the platform UI
// Optional: human-readable preview
preview?: string; // data URI or object URL for images
// Optional: inscription extras
metadata?: Record<string, unknown>; // merged into tag-5 CBOR metadata
developerFee?: BAppDeveloperFee; // override your catalog fee at runtime
inscriptionDefaults?: { // suggest defaults; user can override
parentIds?: string[];
delegateId?: string;
metaprotocol?: string;
postage?: number;
contentEncoding?: 'br' | 'gzip' | 'deflate' | 'identity';
compressBeforeInscribe?: boolean;
};
}Content types guide
| content type | content field | preview |
|---|---|---|
| image/svg+xml | string (SVG markup) | rendered as <img> |
| text/html;charset=utf-8 | string (HTML doc) | sandboxed <iframe> |
| image/png / image/webp | Blob | rendered as <img> |
| application/json | string (JSON) | formatted <pre> |
| text/plain | string | text <pre> |
BAppProps & types
Every BApp component receives a single prop: onOutput.
// src/types/bapp.ts (already in the project)
interface BAppProps {
onOutput: (output: BAppOutput | null) => void;
}
// Call onOutput(null) to clear the current output
// (e.g. when the user deletes their content)Base template
Copy this template into src/components/bapp/apps/MyBApp.tsx and start customising.
'use client';
import { useState, useEffect } from 'react';
import type { BAppProps } from '@/types/bapp';
/**
* MyBApp — replace this with your creative tool.
*
* Rules:
* 1. Call onOutput() whenever content changes.
* 2. Call onOutput(null) if the user clears their work.
* 3. Do NOT implement an Inscribe button — the runtime adds it.
* 4. Do NOT encode to base64 yourself — the runtime does it.
*/
export default function MyBApp({ onOutput }: BAppProps) {
const [value, setValue] = useState('Hello, Bitcoin.');
useEffect(() => {
if (!value.trim()) {
onOutput(null);
return;
}
// Example: produce an SVG inscription
const svg = `<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 800 400" width="800" height="400">
<rect width="800" height="400" fill="#0b0b0b"/>
<text x="400" y="210"
font-family="monospace" font-size="48"
fill="#f7931a" text-anchor="middle"
dominant-baseline="middle">${escXml(value)}</text>
</svg>`;
onOutput({
content: svg,
contentType: 'image/svg+xml',
fileName: 'my-inscription.svg',
title: value.slice(0, 40),
});
}, [value, onOutput]);
return (
<div className="space-y-4">
{/* Your tool UI goes here */}
<label className="block">
<span className="text-xs font-mono uppercase tracking-wide text-text-dim">Text</span>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
className="input-base w-full mt-1"
/>
</label>
</div>
);
}
function escXml(s: string) {
return s
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}Register your BApp
After creating your component, add it to the registry and catalog:
// 1. src/components/bapp/registry.tsx
// Add a lazy import:
MyBApp: lazy(() => import('./apps/MyBApp')),
// 2. src/data/bapp-catalog.ts
// Add an entry to the bapps array:
{
id: 'my-bapp',
slug: 'my-bapp',
name: 'My BApp',
description: 'Short description (1-2 sentences).',
icon: '◇', // single character / emoji
creatorName: 'YourName',
creatorAddress: 'bc1p...', // optional: your Bitcoin address
category: 'Art', // see category guide below
tags: ['tag1', 'tag2', 'tag3'],
outputTypes: ['image/svg+xml'],
launchType: 'component',
launchComponent: 'MyBApp', // must match registry key exactly
status: 'beta',
featured: false,
version: '1.0.0',
// Optional developer fee:
monetisation: {
hasDeveloperFee: true,
feeAddress: 'bc1p...',
feeAmountSats: 1000,
feeMode: 'transaction',
},
},Submission checklist
How to submit
There are two ways to get your BApp into the store:
Fastest path to production
Fork the public BApp repo, add your component, and open a PR. Once reviewed it goes live on the store.
- Forkgithub.com/switch-900/BappStore
- Buildfollow the template in /template
- PRopen a pull request with the BApp: prefix
Suggest a bounty
Have a great idea but can't code it? Open a GitHub issue describing what you want built. If the community picks it up and ships it, it goes in the store and they earn the dev fee.
Be specific — what it produces, what controls it needs, who it's for. Clearer specs get built faster.
Submit idea →