Opening Clicked Hyperlinks in the OS Default Browser
The problem in 2026
The SpiceLogic HTML Editor renders your document inside an embedded browser host. By default, a hyperlink click is handled inside that host - the page navigates within the editor in preview mode, or does nothing while the user is authoring. Customers consistently want the opposite: the click should launch the URL in the operating system's default browser - Microsoft Edge, Google Chrome, Brave, Firefox, whatever the user has set as the Windows default.
Historically this was framed as a "Microsoft Internet Explorer" problem. That framing is now obsolete. The modern editor is Chromium-backed, and customers expect their chosen browser to open - not whatever browser the editor itself hosts.
Design mode vs. Preview mode
The editor has three modes - EditorModes.WysiwygDesign, EditorModes.HtmlEdit, and EditorModes.ReadOnlyPreview. Hyperlink clicks behave differently in each: in WysiwygDesign a click places the caret inside the link (no navigation), in HtmlEdit the user is editing raw text so no DOM click happens, and in ReadOnlyPreview the user is previewing the final document - this is where you almost always want a click to launch the OS default browser. The HtmlElementClicked event fires in all three modes; gate the launch behaviour on htmlEditor1.EditorMode == EditorModes.ReadOnlyPreview so the user is never pulled out of the editor mid-edit.
The hook: HtmlElementClicked + FireHtmlElementClickEventFor
The editor exposes a CLR event HtmlElementClicked on WinFormHtmlEditor whose args (SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs.HtmlElementClickedEventArgs) carry the clicked DOM element as ClickedElement (a System.Windows.Forms.HtmlElement), the underlying HtmlElementEventArgs as EventData, and a strongly-typed ElementType of enum HtmlElementTypes (Hyperlink, Image, Table, etc.). To make the event fire only for hyperlink clicks and skip the noise from spans, divs, and paragraphs, set htmlEditor1.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink;.
Inside the handler, cancel the default in-host navigation and launch the URL via System.Diagnostics.Process.Start with UseShellExecute = true. UseShellExecute = true is the part that matters - it tells Windows to resolve the URL through the registered protocol handler for http / https, which is the user's OS default browser.
Full WinForms snippet
using System;
using System.Diagnostics;
using SpiceLogic.HtmlEditor.Abstractions;
using SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs;
// In your form Load handler:
htmlEditor1.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink;
htmlEditor1.HtmlElementClicked += HtmlEditor1_HyperlinkClicked;
private void HtmlEditor1_HyperlinkClicked(object sender, HtmlElementClickedEventArgs e)
{
// Only launch in Preview mode - never while the user edits.
if (htmlEditor1.EditorMode != EditorModes.ReadOnlyPreview) return;
if (e.ElementType != HtmlElementTypes.Hyperlink || e.ClickedElement == null) return;
string href = e.ClickedElement.GetAttribute("href");
if (!TryGetSafeExternalUrl(href, out string safeUrl)) return;
if (e.EventData != null) e.EventData.ReturnValue = false; // cancel in-host nav
Process.Start(new ProcessStartInfo
{
FileName = safeUrl,
UseShellExecute = true // routes through the OS default browser
});
}
private static bool TryGetSafeExternalUrl(string href, out string url)
{
url = null;
if (string.IsNullOrWhiteSpace(href)) return false;
if (href.StartsWith("#")) return false; // in-page anchor
if (href.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
return false; // script injection
if (!Uri.TryCreate(href, UriKind.Absolute, out Uri parsed)) return false;
if (parsed.Scheme == Uri.UriSchemeHttp
|| parsed.Scheme == Uri.UriSchemeHttps
|| parsed.Scheme == Uri.UriSchemeMailto
|| parsed.Scheme == "tel")
{
url = parsed.AbsoluteUri;
return true;
}
return false;
}WPF variant (same idea, different event-args)
If you also ship the WPF control, the same pattern applies on WpfHtmlEditor. The differences: the event args type is SpiceLogic.HtmlEditor.WPF.EditorEventArgs.HtmlElementClickedEventArgs (a RoutedEventArgs), ClickedElement is an mshtml.IHTMLElement so you read the href via e.ClickedElement.getAttribute("href", 0) as string, and to cancel the default navigation you set e.Handled = true; instead of e.EventData.ReturnValue = false;. The TryGetSafeExternalUrl helper is reusable verbatim - it has no UI dependencies.
Edge cases worth handling
- Anchor jumps inside the same document (
<a href="#footnote-3">) - do not launch the browser; let the host scroll. The helper filters these out by checking for a leading#. - javascript: pseudo-URLs - never pass these to
Process.Start. They are a script-execution vector, not a navigation request. - mailto: and tel: - safe to shell-execute. Windows routes them through the user's default mail client / phone-dialer association.
- file:// URLs - the helper rejects these by default; whitelist
Uri.UriSchemeFileif you want them, but treat anyfile:href from untrusted HTML with suspicion. - Relative URLs (
about-us.html) - rejected byUriKind.Absolute; combine withhtmlEditor1.BaseUrlbefore launching if you need them resolved. - Null or empty href - the helper short-circuits on whitespace.
Security note
If the HTML your editor displays comes from untrusted input, validate the URL before calling Process.Start. The whitelist approach in TryGetSafeExternalUrl - check the parsed Uri.Scheme against an explicit allow-list of http, https, mailto, tel - is the correct pattern. Never pass a raw href from the DOM straight to Process.Start with UseShellExecute = true: a malicious document can specify shell:, vbscript:, or a custom protocol handler that runs arbitrary code on the user's machine.
What about the older "Hyperlink in default browser instead of MSIE" page?
The legacy WinForms Programming-category page describes the same idea using Internet Explorer era terminology. Prefer this page for any new code - the HtmlElementClicked + Options.FireHtmlElementClickEventFor + UseShellExecute = true pattern works against the modern Chromium-backed editor and routes through whichever browser the user has set as their Windows default.