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 a recording, so it keeps working when your front end changes underneath it.
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 are real phone recordings 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.