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.