In-app help
Buttons that open a mado demo over your own product, and a help hub your users can search. There are no tooltips over your live UI — a demo is made from pictures of your screens, so it keeps working when your front end changes underneath it.
Build the hub from your captures
On Help hubs in Studio, Build it for me asks a few questions about your product, takes any number of extension captures, screenshot sets or recordings, and drafts a hub: sections, titles, the pages each demo belongs on, and a list of tasks no demo covers yet. You review the draft in the editor and publish it in one click. With AI assist on, the planning step sends your answers, the demos’ titles and step text and their page paths to Anthropic — never a screenshot. With it off, the same flow builds a plainer draft without a model.
Install
One script tag, anywhere in your app shell:
<script async src="https://mado.show/p/help.js" data-hub="hk_your_hub_key"></script>Your key is under Help hubs in Studio.
Buttons
Any element with data-mado-open opens that demo in a modal over the page. data-step deep-links a specific step, and data-vars is a JSON object of personalisation values — {"name":"Dana"} fills {name} placeholders in the demo. data-mado-hub opens the help hub instead of a single demo.
<button data-mado-open="invoice-basics">Show me how</button>
<!-- at a specific step -->
<button data-mado-open="invoice-basics" data-step="s4">Show me how</button>
<!-- personalised -->
<button data-mado-open="invoice-basics" data-vars='{"name":"Dana"}'>Show me how</button>
<!-- the help hub -->
<button data-mado-hub>Help</button>JavaScript
The same actions from your own event handlers. Mado.hub.setDone shows checkmarks in the hub for demos your users have already completed — progress stays in your app; we store nothing about your users.
Mado.open('invoice-basics', { step: 's4', vars: { name: 'Dana' } });
Mado.hub.open({ query: 'export' });
Mado.hub.setPage('/invoices'); // single-page apps; default is location.pathname
Mado.hub.setDone(['invoice-basics']); // checkmarks — progress stays in your app; we store nothing about your users
Mado.on('open', ({ what, demo }) => { /* what: 'demo' or 'hub' */ });
Mado.on('close', ({ what, demo }) => { /* … */ });
Mado.on('search', ({ query, results }) => { /* … */ });
Mado.on('complete', ({ demo }) => { /* … */ });The script loads asynchronously. Calls made before it has loaded can be queued; they run in order once it has. None of these calls ever throws into your page.
// Before help.js has loaded (it loads async), queue calls instead:
window.Mado = window.Mado || {};
Mado.q = Mado.q || [];
Mado.q.push(['hub.setPage', '/invoices']);
Mado.q.push(['on', 'complete', ({ demo }) => { /* … */ }]);To hide the floating button and open the hub only from your own buttons, add data-launcher="none" to the script tag, or set the launcher to none in Studio.
Page rules
In Studio, give a demo page rules like /invoices or /projects/*/settings; the hub lists matching demos first, based on Mado.hub.setPage or, by default, location.pathname.
Native apps
There's no SDK. Open the hosted hub (https://mado.show/h/:key) or a demo link (https://mado.show/d/:slug) in an in-app browser — the demos themselves show your app's real screens either way.
iOS — Swift
import SafariServices
let url = URL(string: "https://mado.show/d/invoice-basics")!
let safari = SFSafariViewController(url: url)
present(safari, animated: true)Android — Kotlin
import androidx.browser.customtabs.CustomTabsIntent
val intent = CustomTabsIntent.Builder().build()
intent.launchUrl(context, Uri.parse("https://mado.show/d/invoice-basics"))React Native
import * as WebBrowser from 'expo-web-browser';
import { Linking } from 'react-native';
async function openDemo(slug: string) {
const url = `https://mado.show/d/${slug}`;
try {
await WebBrowser.openBrowserAsync(url);
} catch {
await Linking.openURL(url); // fallback if expo-web-browser isn't available
}
}Flutter
import 'package:url_launcher/url_launcher.dart';
await launchUrl(
Uri.parse('https://mado.show/d/invoice-basics'),
mode: LaunchMode.inAppBrowserView,
);Privacy
No cookies, no local storage. Searches are stored for 90 days, lower-cased, capped at 80 characters, and shown to you only as counts.
Content Security Policy
If your app sets a CSP, allow:
script-src https://mado.show;
frame-src https://mado.show;
connect-src https://mado.show https://t.mado.show;Size
help.js is under 4 KB gzipped and loads nothing until your page is idle. The search panel loads on first open.