JavaScript Navigator registerProtocolHandler() Method

Beginner
⏱️ 12 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
🔒 Secure context
Limited availability

What You’ll Learn

navigator.registerProtocolHandler() lets a web app become a handler for schemes like mailto:, tel:, or a custom web+ protocol. Learn the scheme + URL template with %s, HTTPS same-origin rules, five examples, and try-it labs.

01

Kind

Method

02

Returns

undefined

03

Status

Limited (not Baseline)

04

Context

Secure (HTTPS)

05

Template

Must include %s

06

Typical use

Webmail / VoIP apps

Introduction

When someone clicks mailto:hello@example.com, the browser usually opens a desktop mail app. A web-based protocol handler lets your site join that process.

Call registerProtocolHandler(scheme, url) from your HTTPS app. Later, when the user activates a matching link, the browser fills your template URL (replacing %s) and navigates there — often after asking for permission.

💡
No Dep / Exp / Non-standard banner

MDN marks Limited availability and a secure context, but not Deprecated, Experimental, or Non-standard. Always feature-detect and handle browser differences.

Understanding the registerProtocolHandler() Method

  • Two argumentsscheme (protocol name) and handler url template.
  • %s required — placeholder for the activated link URL (escaped).
  • HTTPS + same origin — handler URL must be https and match the registering page origin.
  • Allowed schemes — listed schemes like mailto, tel, sms, or custom web+… names.
  • Custom schemes — must start with web+, then at least one lowercase ASCII letter, only lowercase letters after the prefix.
  • Return valueundefined (side-effect API; browser may prompt the user).

📝 Syntax

General form of the method:

JavaScript
navigator.registerProtocolHandler(scheme, url)

Parameters

  • scheme — protocol to handle (for example "mailto" or "web+burger").
  • url — HTTPS same-origin template containing %s.

Return value

  • undefined.

Common exceptions

  • SecurityError — invalid / blocked scheme, non-https handler, or wrong origin.
  • SyntaxError — missing %s in the template URL.

MDN-style mailto example

JavaScript
navigator.registerProtocolHandler(
  "mailto",
  "https://mail.example.org/?to=%s"
);
// Later, mailto:webmaster@example.com may open:
// https://mail.example.org/?to=mailto:webmaster@example.com

⚡ Quick Reference

GoalCode
Feature detecttypeof navigator.registerProtocolHandler === "function"
Register mailtonavigator.registerProtocolHandler("mailto", "https://…/?to=%s")
Custom schemenavigator.registerProtocolHandler("web+app", "https://…/?q=%s")
Secure context?window.isSecureContext
Template checkurl.includes("%s")
Simulate filltemplate.replace("%s", encodeURIComponent(href))

🔍 At a Glance

Four facts to remember about navigator.registerProtocolHandler().

Args
scheme, url

Template needs %s

Status
Limited

Not Baseline

Context
HTTPS

Same origin

Returns
undefined

May prompt user

📋 Desktop Handler vs Web Handler

Desktop appWeb registerProtocolHandler
ExampleOutlook / Mail for mailto:Webmail at https://mail.example.org/?to=%s
InstallOS associationSite registers; browser may ask user
NavigationOpens native appOpens your HTTPS page with substituted URL
SupportOS-levelLimited browsers; feature-detect

Examples Gallery

Try It Yourself demos detect support, validate templates, and catch errors. Full registration only succeeds on HTTPS with a same-origin handler URL and an allowed scheme — browsers may also require a user gesture.

📚 Getting Started

Detect the API and understand secure-context requirements.

Example 1 — Feature Detection

Check the method and whether the page is a secure context.

JavaScript
const lines = [
  "registerProtocolHandler: " +
    (typeof navigator.registerProtocolHandler === "function"
      ? "available"
      : "missing"),
  "isSecureContext: " + window.isSecureContext,
  "origin: " + location.origin
];
console.log(lines.join("\n"));
Try It Yourself

How It Works

If either check fails, keep your normal “open in app” / download fallback UI.

Example 2 — Register mailto (MDN Style)

Webmail pattern: handle mailto: links with an HTTPS template. Use your real origin in production.

JavaScript
function registerMailtoHandler() {
  if (typeof navigator.registerProtocolHandler !== "function") {
    return "API not supported";
  }
  if (!window.isSecureContext) {
    return "Needs HTTPS / localhost";
  }
  try {
    // Handler URL must be same-origin as this page in real apps
    const handler =
      location.origin + "/mail/compose?to=%s";
    navigator.registerProtocolHandler("mailto", handler);
    return "Registration requested for mailto → " + handler;
  } catch (err) {
    return "Error: " + err.name + " — " + err.message;
  }
}

console.log(registerMailtoHandler());
Try It Yourself

How It Works

After approval, a mailto: click can navigate to your compose page with the address in the query.

📈 Practical Patterns

Custom web+ schemes, template filling, and safe wrappers.

Example 3 — Custom web+ Scheme

MDN-style custom protocol such as web+burger.

JavaScript
function registerCustomScheme() {
  if (typeof navigator.registerProtocolHandler !== "function") {
    return "API not supported";
  }
  try {
    const handler = location.origin + "/open?item=%s";
    navigator.registerProtocolHandler("web+demo", handler);
    return "Requested web+demo → " + handler;
  } catch (err) {
    return "Error: " + err.name;
  }
}

console.log(registerCustomScheme());
// Link example: <a href="web+demo:cheeseburger">open</a>
Try It Yourself

How It Works

Custom names must follow the web+ + lowercase letters rules from the HTML standard.

Example 4 — Understand the %s Substitution

Simulate how the browser fills the template (for learning — not a substitute for the API).

JavaScript
function previewHandlerUrl(template, activatedHref) {
  if (!template.includes("%s")) {
    return { ok: false, reason: "missing-%s" };
  }
  return {
    ok: true,
    result: template.replace("%s", encodeURIComponent(activatedHref))
  };
}

const template = "https://mail.example.org/?to=%s";
const activated = "mailto:webmaster@example.com";
console.log(JSON.stringify(previewHandlerUrl(template, activated)));
Try It Yourself

How It Works

Your handler page should parse the query and open compose / call UI from that value.

Example 5 — Safe Register Helper

Validate inputs, require a user gesture path, and catch SecurityError / SyntaxError.

JavaScript
function registerProtocolHandlerSafe(scheme, url) {
  if (typeof navigator.registerProtocolHandler !== "function") {
    return { ok: false, reason: "unsupported" };
  }
  if (!window.isSecureContext) {
    return { ok: false, reason: "insecure-context" };
  }
  if (!url.includes("%s")) {
    return { ok: false, reason: "missing-%s" };
  }
  try {
    navigator.registerProtocolHandler(scheme, url);
    return { ok: true, reason: "requested" };
  } catch (err) {
    return { ok: false, reason: err.name };
  }
}

// Demo: intentionally missing %s → SyntaxError path / pre-check
console.log(JSON.stringify(
  registerProtocolHandlerSafe("mailto", location.origin + "/mail?to=")
));
console.log(JSON.stringify(
  registerProtocolHandlerSafe("mailto", location.origin + "/mail?to=%s")
));
Try It Yourself

How It Works

Pre-checking %s avoids a SyntaxError; try/catch still covers origin and scheme blocks.

🚀 Common Use Cases

  • Webmail — handle mailto: links inside your compose UI.
  • VoIP / chat — handle tel:, sms:, sip:, or xmpp:.
  • Custom deep linksweb+yourapp for product-specific URLs.
  • PWA install flows — offer “Use this site for mailto” after install.
  • Magnet / calendar — schemes like magnet or webcal where allowed.

🧠 How registerProtocolHandler() Works

1

Register from HTTPS

Pass an allowed scheme and a same-origin template with %s.

Register
2

User may confirm

The browser can ask permission at register time or on first use.

Permission
3

User clicks a scheme link

For example mailto:… or web+demo:….

Activate
4

Browser opens your template

%s is replaced; your page parses the parameter and continues.

📝 Notes

Limited Browser Support

navigator.registerProtocolHandler() is not Baseline. Support is strongest in some Chromium-based browsers on HTTPS. Always feature-detect, use a same-origin HTTPS template with %s, and keep a fallback for unsupported engines.

Limited · Not Baseline

Navigator.registerProtocolHandler()

Register mailto / tel / web+ handlers with an HTTPS %s URL template.

Limited Not Baseline
Google Chrome Supported with secure context / scheme rules
Supported
Microsoft Edge Follow Chromium protocol handler support
Supported
Opera Follow Chromium where available
Supported
Mozilla Firefox Check current compatibility — feature-detect
Limited
Apple Safari Typically unavailable — feature-detect
Unavailable
Internet Explorer No modern registerProtocolHandler path
Unavailable
registerProtocolHandler() Limited

Bottom line: Detect the method, register from a user gesture on HTTPS, include %s, catch SecurityError/SyntaxError, and offer a normal link fallback.

Conclusion

navigator.registerProtocolHandler() turns your HTTPS app into a handler for mailto:, tel:, or custom web+ links. Register a same-origin template with %s, expect a user confirmation, and always provide a fallback when the API is missing.

Continue with requestMediaKeySystemAccess(), canShare(), or the JavaScript hub.

💡 Best Practices

✅ Do

  • Feature-detect before registering
  • Use HTTPS same-origin templates with %s
  • Register from a clear button click
  • Catch SecurityError and SyntaxError
  • Parse the substituted URL carefully on the handler page

❌ Don’t

  • Try to register https / about / browser-owned schemes
  • Omit %s from the template
  • Point the handler at a different origin
  • Assume Baseline support in Safari / all engines
  • Spam registration on every page load

Key Takeaways

Knowledge Unlocked

Five things to remember about registerProtocolHandler()

Register scheme handlers with an HTTPS %s template — limited, secure-context API.

5
Core concepts
🔒 02

Needs

HTTPS + %s

Same origin
📊 03

Support

Limited

Not Baseline
👋 04

UX

user may confirm

Gesture
🛡 05

Errors

Security / Syntax

try/catch

❓ Frequently Asked Questions

It lets a website register itself as a handler for a URL scheme such as mailto:, tel:, or a custom web+… scheme. When the user opens that kind of link, the browser can navigate to your HTTPS template URL with the original link substituted for %s.
No. MDN does not mark it Deprecated, Experimental, or Non-standard. It is Limited availability (not Baseline) and requires a secure context. No status banner is required.
An https URL of the same origin as the registering page, containing the %s placeholder that the browser replaces with the escaped URL being handled.
No. Schemes the browser already handles itself are blocked. Use an allowed scheme from the HTML list, or a custom scheme that begins with web+ and uses lowercase ASCII letters.
Often yes — either when registering or when the user first activates a matching link. Always call registration from a clear user gesture (for example a button click).
SecurityError when the scheme or origin/https rules fail, and SyntaxError when %s is missing from the template URL. Feature-detect and wrap calls in try/catch.
Did you know?

Custom schemes must start with web+. That prefix exists so sites cannot hijack important browser schemes like https: or about:.

Explore requestMediaKeySystemAccess() Next

Start Encrypted Media Extensions with Clear Key or DRM.

requestMediaKeySystemAccess() →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful